├── bootstrap ├── api │ ├── .htaccess │ ├── Slim │ │ ├── Exception │ │ │ ├── Pass.php │ │ │ ├── RequestSlash.php │ │ │ └── Stop.php │ │ ├── Http │ │ │ ├── Cookie.php │ │ │ ├── CookieJar.php │ │ │ ├── Request.php │ │ │ ├── Response.php │ │ │ └── Uri.php │ │ ├── Log.php │ │ ├── Logger.php │ │ ├── Route.php │ │ ├── Router.php │ │ ├── Session │ │ │ ├── Flash.php │ │ │ ├── Handler.php │ │ │ └── Handler │ │ │ │ └── Cookies.php │ │ ├── Slim.php │ │ └── View.php │ ├── index.php │ └── upload.php ├── css │ ├── bootstrap-responsive.css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── docs.css │ └── styles.css ├── img │ ├── blog.png │ ├── discuss.png │ ├── download.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ └── twitter.png ├── index.html ├── js │ ├── main.js │ ├── memorystore.js │ ├── models │ │ └── models.js │ ├── utils.js │ └── views │ │ ├── about.js │ │ ├── header.js │ │ ├── paginator.js │ │ ├── winedetails.js │ │ └── winelist.js ├── lib │ ├── backbone-min.js │ ├── bootstrap-dropdown.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-1.7.2.min.js │ └── underscore-min.js ├── pics │ ├── argiano.jpg │ ├── block_nine.jpg │ ├── bodega_lurton.jpg │ ├── bouscat.jpg │ ├── calera.jpg │ ├── capineto.png │ ├── caronne.jpg │ ├── dinastia.jpg │ ├── domaine_serene.jpg │ ├── ex_umbris.jpg │ ├── fourvines.jpg │ ├── generic.jpg │ ├── hugel.jpg │ ├── lan_rioja.jpg │ ├── le_doyenne.jpg │ ├── margerum.jpg │ ├── momo.jpg │ ├── morizottes.jpg │ ├── petalos.jpg │ ├── ponzi.jpg │ ├── quivira.jpg │ ├── rex_hill.jpg │ ├── saint_cosme.jpg │ ├── shafer.jpg │ ├── viticcio.jpg │ └── waterbrook.jpg ├── readme.md └── tpl │ ├── AboutView.html │ ├── HeaderView.html │ ├── WineListItemView.html │ └── WineView.html ├── cellar.sql ├── readme.md └── tutorial ├── api ├── .htaccess ├── Slim │ ├── Exception │ │ ├── Pass.php │ │ ├── RequestSlash.php │ │ └── Stop.php │ ├── Http │ │ ├── Cookie.php │ │ ├── CookieJar.php │ │ ├── Request.php │ │ ├── Response.php │ │ └── Uri.php │ ├── Log.php │ ├── Logger.php │ ├── Route.php │ ├── Router.php │ ├── Session │ │ ├── Flash.php │ │ ├── Handler.php │ │ └── Handler │ │ │ └── Cookies.php │ ├── Slim.php │ └── View.php └── index.php ├── css └── styles.css ├── final ├── index.html ├── js │ ├── main.js │ ├── models │ │ └── winemodel.js │ ├── utils.js │ └── views │ │ ├── header.js │ │ ├── winedetails.js │ │ └── winelist.js └── tpl │ ├── header.html │ ├── wine-details.html │ └── wine-list-item.html ├── lib ├── backbone-min.js ├── jquery-1.7.1.min.js └── underscore-min.js ├── mobile ├── android │ └── BackboneCellarAndroid │ │ ├── .classpath │ │ ├── .project │ │ ├── .settings │ │ └── org.eclipse.jdt.core.prefs │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── www │ │ │ ├── css │ │ │ └── styles.css │ │ │ ├── index.html │ │ │ ├── js │ │ │ ├── main.js │ │ │ ├── models │ │ │ │ └── winemodel.js │ │ │ ├── utils.js │ │ │ └── views │ │ │ │ ├── winedetails.js │ │ │ │ └── winelist.js │ │ │ ├── lib │ │ │ ├── backbone-min.js │ │ │ ├── jquery-1.7.1.min.js │ │ │ └── underscore-min.js │ │ │ ├── phonegap-android-1.4.1.js │ │ │ ├── pics │ │ │ ├── block_nine.jpg │ │ │ ├── bodega_lurton.jpg │ │ │ ├── bouscat.jpg │ │ │ ├── domaine_serene.jpg │ │ │ ├── ex_umbris.jpg │ │ │ ├── generic.jpg │ │ │ ├── lan_rioja.jpg │ │ │ ├── le_doyenne.jpg │ │ │ ├── lurton-pinot-gris.jpg │ │ │ ├── margerum.jpg │ │ │ ├── morizottes.jpg │ │ │ ├── rex_hill.jpg │ │ │ ├── saint_cosme.jpg │ │ │ └── viticcio.jpg │ │ │ └── tpl │ │ │ ├── wine-details.html │ │ │ ├── wine-list-item.html │ │ │ └── wine-list.html │ │ ├── bin │ │ ├── BackboneCellarAndroid.apk │ │ ├── classes.dex │ │ ├── org │ │ │ └── coenraets │ │ │ │ └── cellar │ │ │ │ ├── BackboneCellarAndroidActivity.class │ │ │ │ ├── R$attr.class │ │ │ │ ├── R$drawable.class │ │ │ │ ├── R$layout.class │ │ │ │ ├── R$string.class │ │ │ │ ├── R$xml.class │ │ │ │ └── R.class │ │ └── resources.ap_ │ │ ├── default.properties │ │ ├── gen │ │ └── org │ │ │ └── coenraets │ │ │ └── cellar │ │ │ └── R.java │ │ ├── libs │ │ └── phonegap-1.4.1.jar │ │ ├── proguard.cfg │ │ ├── res │ │ ├── drawable-hdpi │ │ │ └── icon.png │ │ ├── drawable-ldpi │ │ │ └── icon.png │ │ ├── drawable-mdpi │ │ │ └── icon.png │ │ ├── layout │ │ │ └── main.xml │ │ ├── values │ │ │ └── strings.xml │ │ └── xml │ │ │ ├── phonegap.xml │ │ │ └── plugins.xml │ │ └── src │ │ └── org │ │ └── coenraets │ │ └── cellar │ │ └── BackboneCellarAndroidActivity.java ├── ios │ └── WineCellar │ │ ├── WineCellar.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── christophe.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── christophe.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── WineCellar 2.xcscheme │ │ │ ├── WineCellar.xcscheme │ │ │ └── xcschememanagement.plist │ │ ├── WineCellar │ │ ├── Classes │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── MainViewController.h │ │ │ ├── MainViewController.m │ │ │ └── MainViewController.xib │ │ ├── PhoneGap.plist │ │ ├── Plugins │ │ │ └── README │ │ ├── Resources │ │ │ ├── Capture.bundle │ │ │ │ ├── controls_bg.png │ │ │ │ ├── controls_bg@2x.png │ │ │ │ ├── controls_bg~ipad.png │ │ │ │ ├── microphone.png │ │ │ │ ├── microphone@2x.png │ │ │ │ ├── microphone~ipad.png │ │ │ │ ├── record_button.png │ │ │ │ ├── record_button@2x.png │ │ │ │ ├── record_button~ipad.png │ │ │ │ ├── recording_bg.png │ │ │ │ ├── recording_bg@2x.png │ │ │ │ ├── recording_bg~ipad.png │ │ │ │ ├── stop_button.png │ │ │ │ ├── stop_button@2x.png │ │ │ │ └── stop_button~ipad.png │ │ │ ├── en.lproj │ │ │ │ └── Localizable.strings │ │ │ ├── es.lproj │ │ │ │ └── Localizable.strings │ │ │ ├── icons │ │ │ │ ├── icon-72.png │ │ │ │ ├── icon.png │ │ │ │ └── icon@2x.png │ │ │ └── splash │ │ │ │ ├── Default.png │ │ │ │ └── Default@2x.png │ │ ├── WineCellar-Info.plist │ │ ├── WineCellar-Prefix.pch │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ └── main.m │ │ └── www │ │ ├── css │ │ └── styles.css │ │ ├── index.html │ │ ├── js │ │ ├── main.js │ │ ├── models │ │ │ └── winemodel.js │ │ ├── utils.js │ │ └── views │ │ │ ├── winedetails.js │ │ │ └── winelist.js │ │ ├── lib │ │ ├── backbone-min.js │ │ ├── jquery-1.7.1.min.js │ │ └── underscore-min.js │ │ ├── phonegap-ios-1.4.1.js │ │ ├── pics │ │ ├── block_nine.jpg │ │ ├── bodega_lurton.jpg │ │ ├── bouscat.jpg │ │ ├── domaine_serene.jpg │ │ ├── ex_umbris.jpg │ │ ├── generic.jpg │ │ ├── lan_rioja.jpg │ │ ├── le_doyenne.jpg │ │ ├── lurton-pinot-gris.jpg │ │ ├── margerum.jpg │ │ ├── morizottes.jpg │ │ ├── rex_hill.jpg │ │ ├── saint_cosme.jpg │ │ └── viticcio.jpg │ │ └── tpl │ │ ├── wine-details.html │ │ ├── wine-list-item.html │ │ └── wine-list.html └── offline │ ├── css │ └── styles.css │ ├── index.html │ ├── js │ ├── main.js │ ├── models │ │ └── winemodel.js │ └── views │ │ ├── winedetails.js │ │ └── winelist.js │ ├── lib │ ├── backbone-min.js │ ├── jquery-1.7.1.min.js │ └── underscore-min.js │ ├── phonegap-ios-1.4.1.js │ ├── pics │ ├── block_nine.jpg │ ├── bodega_lurton.jpg │ ├── bouscat.jpg │ ├── domaine_serene.jpg │ ├── ex_umbris.jpg │ ├── generic.jpg │ ├── lan_rioja.jpg │ ├── le_doyenne.jpg │ ├── lurton-pinot-gris.jpg │ ├── margerum.jpg │ ├── morizottes.jpg │ ├── rex_hill.jpg │ ├── saint_cosme.jpg │ └── viticcio.jpg │ └── tpl │ ├── wine-details.html │ ├── wine-list-item.html │ └── wine-list.html ├── part1 ├── index.html └── js │ └── main.js ├── part2 ├── index.html └── js │ └── main.js ├── part3 ├── index.html └── js │ └── main.js ├── pics ├── argiano.jpg ├── block_nine.jpg ├── bodega_lurton.jpg ├── bouscat.jpg ├── calera.jpg ├── capineto.png ├── caronne.jpg ├── dinastia.jpg ├── domaine_serene.jpg ├── ex_umbris.jpg ├── fourvines.jpg ├── generic.jpg ├── hugel.jpg ├── lan_rioja.jpg ├── le_doyenne.jpg ├── lurton-pinot-gris.jpg ├── margerum.jpg ├── momo.jpg ├── morizottes.jpg ├── petalos.jpg ├── ponzi.jpg ├── quivira.jpg ├── rex_hill.jpg ├── saint_cosme.jpg ├── shafer.jpg ├── viticcio.jpg └── waterbrook.jpg └── readme.md /bootstrap/api/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | # Some hosts may require you to use the `RewriteBase` directive. 4 | # If you need to use the `RewriteBase` directive, it should be the 5 | # absolute physical path to the directory that contains this htaccess file. 6 | # 7 | # RewriteBase / 8 | 9 | RewriteCond %{REQUEST_FILENAME} !-f 10 | RewriteRule ^(.*)$ index.php [QSA,L] -------------------------------------------------------------------------------- /bootstrap/api/Slim/Exception/Pass.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Pass Exception 35 | * 36 | * This Exception will cause the Router::dispatch method 37 | * to skip the current matching route and continue to the next 38 | * matching route. If no subsequent routes are found, a 39 | * HTTP 404 Not Found response will be sent to the client. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Exception_Pass extends Exception {} -------------------------------------------------------------------------------- /bootstrap/api/Slim/Exception/RequestSlash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request Slash Exception 35 | * 36 | * This Exception is thrown when Slim detects a matching route 37 | * (defined with a trailing slash) and the HTTP request 38 | * matches the route but does not have a trailing slash. This 39 | * exception will be caught in `Slim::run` and trigger a 301 redirect 40 | * to the same resource URI with a trailing slash. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_Exception_RequestSlash extends Exception {} -------------------------------------------------------------------------------- /bootstrap/api/Slim/Exception/Stop.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Stop Exception 35 | * 36 | * This Exception is thrown when the Slim application needs to abort 37 | * processing and return control flow to the outer PHP script. 38 | * 39 | * @package Slim 40 | * @author Josh Lockhart 41 | * @since Version 1.0 42 | */ 43 | class Slim_Exception_Stop extends Exception {} -------------------------------------------------------------------------------- /bootstrap/api/Slim/Http/Uri.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Uri 35 | * 36 | * Parses base uri and application uri from Request. 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Uri { 43 | 44 | /** 45 | * @var string "https" or "http" 46 | */ 47 | protected static $scheme; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected static $baseUri; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected static $uri; 58 | 59 | /** 60 | * @var string The URI query string, excluding leading "?" 61 | */ 62 | protected static $queryString; 63 | 64 | /** 65 | * Get Base URI without trailing slash 66 | * @param bool $reload Force reparse the base URI? 67 | * @return string 68 | */ 69 | public static function getBaseUri( $reload = false ) { 70 | if ( $reload || is_null(self::$baseUri) ) { 71 | $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; //Full Request URI 72 | $scriptName = $_SERVER['SCRIPT_NAME']; //Script path from docroot 73 | $baseUri = strpos($requestUri, $scriptName) === 0 ? $scriptName : str_replace('\\', '/', dirname($scriptName)); 74 | self::$baseUri = rtrim($baseUri, '/'); 75 | } 76 | return self::$baseUri; 77 | } 78 | 79 | /** 80 | * Get URI with leading slash 81 | * @param bool $reload Force reparse the URI? 82 | * @return string 83 | * @throws RuntimeException If unable if unable to determine URI 84 | */ 85 | public static function getUri( $reload = false ) { 86 | if ( $reload || is_null(self::$uri) ) { 87 | $uri = ''; 88 | if ( !empty($_SERVER['PATH_INFO']) ) { 89 | $uri = $_SERVER['PATH_INFO']; 90 | } else { 91 | if ( isset($_SERVER['REQUEST_URI']) ) { 92 | $uri = parse_url(self::getScheme() . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); 93 | } else if ( isset($_SERVER['PHP_SELF']) ) { 94 | $uri = $_SERVER['PHP_SELF']; 95 | } else { 96 | throw new RuntimeException('Unable to detect request URI'); 97 | } 98 | } 99 | if ( self::getBaseUri() !== '' && strpos($uri, self::getBaseUri()) === 0 ) { 100 | $uri = substr($uri, strlen(self::getBaseUri())); 101 | } 102 | self::$uri = '/' . ltrim($uri, '/'); 103 | } 104 | return self::$uri; 105 | } 106 | 107 | /** 108 | * Get URI Scheme 109 | * @param bool $reload For reparse the URL scheme? 110 | * @return string "https" or "http" 111 | */ 112 | public static function getScheme( $reload = false ) { 113 | if ( $reload || is_null(self::$scheme) ) { 114 | self::$scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https'; 115 | } 116 | return self::$scheme; 117 | } 118 | 119 | /** 120 | * Get URI Query String 121 | * @param bool $reload For reparse the URL query string? 122 | * @return string 123 | */ 124 | public static function getQueryString( $reload = false ) { 125 | if ( $reload || is_null(self::$queryString) ) { 126 | self::$queryString = $_SERVER['QUERY_STRING']; 127 | } 128 | return self::$queryString; 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /bootstrap/api/Slim/Log.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Log Adapter 35 | * 36 | * This is an adapter for your own custom Logger. This adapter assumes 37 | * your custom Logger provides the following public instance methods: 38 | * 39 | * debug( mixed $object ) 40 | * info( mixed $object ) 41 | * warn( mixed $object ) 42 | * error( mixed $object ) 43 | * fatal( mixed $object ) 44 | * 45 | * This class assumes nothing else about your custom Logger, so you are free 46 | * to use Apache's Log4PHP logger or any other log class that, at the 47 | * very least, implements the five public instance methods shown above. 48 | * 49 | * @package Slim 50 | * @author Josh Lockhart 51 | * @since Version 1.0 52 | */ 53 | class Slim_Log { 54 | 55 | /** 56 | * @var mixed An object that implements expected Logger interface 57 | */ 58 | protected $logger; 59 | 60 | /** 61 | * @var bool Enable logging? 62 | */ 63 | protected $enabled; 64 | 65 | /** 66 | * Constructor 67 | */ 68 | public function __construct() { 69 | $this->enabled = true; 70 | } 71 | 72 | /** 73 | * Enable or disable logging 74 | * @param bool $enabled 75 | * @return void 76 | */ 77 | public function setEnabled( $enabled ) { 78 | if ( $enabled ) { 79 | $this->enabled = true; 80 | } else { 81 | $this->enabled = false; 82 | } 83 | } 84 | 85 | /** 86 | * Is logging enabled? 87 | * @return bool 88 | */ 89 | public function isEnabled() { 90 | return $this->enabled; 91 | } 92 | 93 | /** 94 | * Log debug message 95 | * @param mixed $object 96 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 97 | */ 98 | public function debug( $object ) { 99 | return isset($this->logger) && $this->isEnabled() ? $this->logger->debug($object) : false; 100 | } 101 | 102 | /** 103 | * Log info message 104 | * @param mixed $object 105 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 106 | */ 107 | public function info( $object ) { 108 | return isset($this->logger) && $this->isEnabled() ? $this->logger->info($object) : false; 109 | } 110 | 111 | /** 112 | * Log warn message 113 | * @param mixed $object 114 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 115 | */ 116 | public function warn( $object ) { 117 | return isset($this->logger) && $this->isEnabled() ? $this->logger->warn($object) : false; 118 | } 119 | 120 | /** 121 | * Log error message 122 | * @param mixed $object 123 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 124 | */ 125 | public function error( $object ) { 126 | return isset($this->logger) && $this->isEnabled() ? $this->logger->error($object) : false; 127 | } 128 | 129 | /** 130 | * Log fatal message 131 | * @param mixed $object 132 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 133 | */ 134 | public function fatal( $object ) { 135 | return isset($this->logger) && $this->isEnabled() ? $this->logger->fatal($object) : false; 136 | } 137 | 138 | /** 139 | * Set Logger 140 | * @param mixed $logger 141 | * @return void 142 | */ 143 | public function setLogger( $logger ) { 144 | $this->logger = $logger; 145 | } 146 | 147 | /** 148 | * Get Logger 149 | * @return mixed 150 | */ 151 | public function getLogger() { 152 | return $this->logger; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /bootstrap/api/Slim/Session/Handler.php: -------------------------------------------------------------------------------- 1 | app = $app; 58 | return session_set_save_handler( 59 | array($this, 'open'), 60 | array($this, 'close'), 61 | array($this, 'read'), 62 | array($this, 'write'), 63 | array($this, 'destroy'), 64 | array($this, 'gc') 65 | ); 66 | } 67 | 68 | /** 69 | * Open session 70 | * 71 | * @param string $savePath 72 | * @param string $sessionName 73 | * @return mixed 74 | */ 75 | abstract public function open( $savePath, $sessionName ); 76 | 77 | /** 78 | * Close session 79 | * 80 | * @return mixed 81 | */ 82 | abstract public function close(); 83 | 84 | /** 85 | * Read session data with ID 86 | * 87 | * @param string $id The session identifier 88 | * @return string 89 | */ 90 | abstract public function read( $id ); 91 | 92 | /** 93 | * Write session data with ID 94 | * 95 | * The "write" handler is not executed until after the output stream is 96 | * closed. Thus, output from debugging statements in the "write" handler 97 | * will never be seen in the browser. If debugging output is necessary, it 98 | * is suggested that the debug output be written to a file instead. 99 | * 100 | * @param string $id The session identifier 101 | * @param mixed $sessionData The session data 102 | * @return mixed 103 | */ 104 | abstract public function write( $id, $sessionData ); 105 | 106 | /** 107 | * Destroy session with ID 108 | * 109 | * @param string $id The session identifier 110 | * @return mixed 111 | */ 112 | abstract public function destroy( $id ); 113 | 114 | /** 115 | * Session garbage collection 116 | * 117 | * Executed when the PHP session garbage collector is invoked; should 118 | * remove all session data older than the `$maxLifetime`. 119 | * 120 | * @param int $maxLifetime 121 | * @return mixed 122 | */ 123 | abstract public function gc( $maxLifetime ); 124 | 125 | } -------------------------------------------------------------------------------- /bootstrap/api/Slim/Session/Handler/Cookies.php: -------------------------------------------------------------------------------- 1 | app->getEncryptedCookie($id); 57 | } 58 | 59 | public function write( $id, $sessionData ) { 60 | $this->app->setEncryptedCookie($id, $sessionData, 0); 61 | } 62 | 63 | public function destroy( $id ) { 64 | $this->app->deleteCookie($id); 65 | } 66 | 67 | public function gc( $maxLifetime ) { 68 | return true; //Not used 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /bootstrap/api/index.php: -------------------------------------------------------------------------------- 1 | get('/wines', 'getWines'); 8 | $app->get('/wines/:id', 'getWine'); 9 | $app->get('/wines/search/:query', 'findByName'); 10 | $app->post('/wines', 'addWine'); 11 | $app->put('/wines/:id', 'updateWine'); 12 | $app->delete('/wines/:id', 'deleteWine'); 13 | 14 | $app->run(); 15 | 16 | function getWines() { 17 | $sql = "select * FROM wine ORDER BY name"; 18 | try { 19 | $db = getConnection(); 20 | $stmt = $db->query($sql); 21 | $wines = $stmt->fetchAll(PDO::FETCH_OBJ); 22 | $db = null; 23 | // echo '{"wine": ' . json_encode($wines) . '}'; 24 | echo json_encode($wines); 25 | } catch(PDOException $e) { 26 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 27 | } 28 | } 29 | 30 | function getWine($id) { 31 | $sql = "SELECT * FROM wine WHERE id=:id"; 32 | try { 33 | $db = getConnection(); 34 | $stmt = $db->prepare($sql); 35 | $stmt->bindParam("id", $id); 36 | $stmt->execute(); 37 | $wine = $stmt->fetchObject(); 38 | $db = null; 39 | echo json_encode($wine); 40 | } catch(PDOException $e) { 41 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 42 | } 43 | } 44 | 45 | function addWine() { 46 | error_log('addWine\n', 3, '/var/tmp/php.log'); 47 | $request = Slim::getInstance()->request(); 48 | $wine = json_decode($request->getBody()); 49 | $sql = "INSERT INTO wine (name, grapes, country, region, year, description, picture) VALUES (:name, :grapes, :country, :region, :year, :description, :picture)"; 50 | try { 51 | $db = getConnection(); 52 | $stmt = $db->prepare($sql); 53 | $stmt->bindParam("name", $wine->name); 54 | $stmt->bindParam("grapes", $wine->grapes); 55 | $stmt->bindParam("country", $wine->country); 56 | $stmt->bindParam("region", $wine->region); 57 | $stmt->bindParam("year", $wine->year); 58 | $stmt->bindParam("description", $wine->description); 59 | $stmt->bindParam("picture", $wine->picture); 60 | $stmt->execute(); 61 | $wine->id = $db->lastInsertId(); 62 | $db = null; 63 | echo json_encode($wine); 64 | } catch(PDOException $e) { 65 | error_log($e->getMessage(), 3, '/var/tmp/php.log'); 66 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 67 | } 68 | } 69 | 70 | function updateWine($id) { 71 | $request = Slim::getInstance()->request(); 72 | $body = $request->getBody(); 73 | $wine = json_decode($body); 74 | $sql = "UPDATE wine SET name=:name, grapes=:grapes, country=:country, region=:region, year=:year, description=:description, picture=:picture WHERE id=:id"; 75 | try { 76 | $db = getConnection(); 77 | $stmt = $db->prepare($sql); 78 | $stmt->bindParam("name", $wine->name); 79 | $stmt->bindParam("grapes", $wine->grapes); 80 | $stmt->bindParam("country", $wine->country); 81 | $stmt->bindParam("region", $wine->region); 82 | $stmt->bindParam("year", $wine->year); 83 | $stmt->bindParam("description", $wine->description); 84 | $stmt->bindParam("picture", $wine->picture); 85 | $stmt->bindParam("id", $id); 86 | $stmt->execute(); 87 | $db = null; 88 | echo json_encode($wine); 89 | } catch(PDOException $e) { 90 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 91 | } 92 | } 93 | 94 | function deleteWine($id) { 95 | $sql = "DELETE FROM wine WHERE id=:id"; 96 | try { 97 | $db = getConnection(); 98 | $stmt = $db->prepare($sql); 99 | $stmt->bindParam("id", $id); 100 | $stmt->execute(); 101 | $db = null; 102 | } catch(PDOException $e) { 103 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 104 | } 105 | } 106 | 107 | function findByName($query) { 108 | $sql = "SELECT * FROM wine WHERE UPPER(name) LIKE :query ORDER BY name"; 109 | try { 110 | $db = getConnection(); 111 | $stmt = $db->prepare($sql); 112 | $query = "%".$query."%"; 113 | $stmt->bindParam("query", $query); 114 | $stmt->execute(); 115 | $wines = $stmt->fetchAll(PDO::FETCH_OBJ); 116 | $db = null; 117 | echo json_encode($wines); 118 | } catch(PDOException $e) { 119 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 120 | } 121 | } 122 | 123 | function getConnection() { 124 | $dbhost="127.0.0.1"; 125 | $dbuser="root"; 126 | $dbpass=""; 127 | $dbname="cellar"; 128 | $dbh = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass); 129 | $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 130 | return $dbh; 131 | } 132 | 133 | ?> -------------------------------------------------------------------------------- /bootstrap/api/upload.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/css/styles.css: -------------------------------------------------------------------------------- 1 | a.plain:link 2 | { color: #333; text-decoration: none } 3 | a.plain:active 4 | { color: #333; text-decoration: none } 5 | a.plain:visited 6 | { color: #333; text-decoration: none } 7 | a.plain:hover 8 | { color: #333; text-decoration: none } 9 | 10 | .status-bar { 11 | height: 60px; 12 | } 13 | 14 | .about-icon { 15 | float: left; 16 | margin-right: 12px; 17 | } -------------------------------------------------------------------------------- /bootstrap/img/blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/blog.png -------------------------------------------------------------------------------- /bootstrap/img/discuss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/discuss.png -------------------------------------------------------------------------------- /bootstrap/img/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/download.png -------------------------------------------------------------------------------- /bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /bootstrap/img/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/img/twitter.png -------------------------------------------------------------------------------- /bootstrap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Backbone Cellar 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /bootstrap/js/main.js: -------------------------------------------------------------------------------- 1 | var AppRouter = Backbone.Router.extend({ 2 | 3 | routes: { 4 | "" : "list", 5 | "wines/page/:page" : "list", 6 | "wines/add" : "addWine", 7 | "wines/:id" : "wineDetails", 8 | "about" : "about" 9 | }, 10 | 11 | initialize: function () { 12 | this.headerView = new HeaderView(); 13 | $('.header').html(this.headerView.el); 14 | }, 15 | 16 | list: function(page) { 17 | var p = page ? parseInt(page, 10) : 1; 18 | var wineList = new WineCollection(); 19 | wineList.fetch({success: function(){ 20 | $("#content").html(new WineListView({model: wineList, page: p}).el); 21 | }}); 22 | this.headerView.selectMenuItem('home-menu'); 23 | }, 24 | 25 | wineDetails: function (id) { 26 | var wine = new Wine({id: id}); 27 | wine.fetch({success: function(){ 28 | $("#content").html(new WineView({model: wine}).el); 29 | }}); 30 | this.headerView.selectMenuItem(); 31 | }, 32 | 33 | addWine: function() { 34 | var wine = new Wine(); 35 | $('#content').html(new WineView({model: wine}).el); 36 | this.headerView.selectMenuItem('add-menu'); 37 | }, 38 | 39 | about: function () { 40 | if (!this.aboutView) { 41 | this.aboutView = new AboutView(); 42 | } 43 | $('#content').html(this.aboutView.el); 44 | this.headerView.selectMenuItem('about-menu'); 45 | } 46 | 47 | }); 48 | 49 | utils.loadTemplate(['HeaderView', 'WineView', 'WineListItemView', 'AboutView'], function() { 50 | app = new AppRouter(); 51 | Backbone.history.start(); 52 | }); -------------------------------------------------------------------------------- /bootstrap/js/models/models.js: -------------------------------------------------------------------------------- 1 | window.Wine = Backbone.Model.extend({ 2 | 3 | urlRoot: "api/wines", 4 | 5 | initialize: function () { 6 | this.validators = {}; 7 | 8 | this.validators.name = function (value) { 9 | return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a name"}; 10 | }; 11 | 12 | this.validators.grapes = function (value) { 13 | return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a grape variety"}; 14 | }; 15 | 16 | this.validators.country = function (value) { 17 | return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a country"}; 18 | }; 19 | }, 20 | 21 | validateItem: function (key) { 22 | return (this.validators[key]) ? this.validators[key](this.get(key)) : {isValid: true}; 23 | }, 24 | 25 | // TODO: Implement Backbone's standard validate() method instead. 26 | validateAll: function () { 27 | 28 | var messages = {}; 29 | 30 | for (var key in this.validators) { 31 | if(this.validators.hasOwnProperty(key)) { 32 | var check = this.validators[key](this.get(key)); 33 | if (check.isValid === false) { 34 | messages[key] = check.message; 35 | } 36 | } 37 | } 38 | 39 | return _.size(messages) > 0 ? {isValid: false, messages: messages} : {isValid: true}; 40 | }, 41 | 42 | defaults: { 43 | id: null, 44 | name: "", 45 | grapes: "", 46 | country: "USA", 47 | region: "California", 48 | year: "", 49 | description: "", 50 | picture: null 51 | } 52 | }); 53 | 54 | window.WineCollection = Backbone.Collection.extend({ 55 | 56 | model: Wine, 57 | 58 | url: "api/wines" 59 | 60 | }); -------------------------------------------------------------------------------- /bootstrap/js/utils.js: -------------------------------------------------------------------------------- 1 | window.utils = { 2 | 3 | // Asynchronously load templates located in separate .html files 4 | loadTemplate: function(views, callback) { 5 | 6 | var deferreds = []; 7 | 8 | $.each(views, function(index, view) { 9 | if (window[view]) { 10 | deferreds.push($.get('tpl/' + view + '.html', function(data) { 11 | window[view].prototype.template = _.template(data); 12 | })); 13 | } else { 14 | alert(view + " not found"); 15 | } 16 | }); 17 | 18 | $.when.apply(null, deferreds).done(callback); 19 | }, 20 | 21 | uploadFile: function (file, callbackSuccess) { 22 | var self = this; 23 | var data = new FormData(); 24 | data.append('file', file); 25 | $.ajax({ 26 | url: 'api/upload.php', 27 | type: 'POST', 28 | data: data, 29 | processData: false, 30 | cache: false, 31 | contentType: false 32 | }) 33 | .done(function () { 34 | console.log(file.name + " uploaded successfully"); 35 | callbackSuccess(); 36 | }) 37 | .fail(function () { 38 | self.showAlert('Error!', 'An error occurred while uploading ' + file.name, 'alert-error'); 39 | }); 40 | }, 41 | 42 | displayValidationErrors: function (messages) { 43 | for (var key in messages) { 44 | if (messages.hasOwnProperty(key)) { 45 | this.addValidationError(key, messages[key]); 46 | } 47 | } 48 | this.showAlert('Warning!', 'Fix validation errors and try again', 'alert-warning'); 49 | }, 50 | 51 | addValidationError: function (field, message) { 52 | var controlGroup = $('#' + field).parent().parent(); 53 | controlGroup.addClass('error'); 54 | $('.help-inline', controlGroup).html(message); 55 | }, 56 | 57 | removeValidationError: function (field) { 58 | var controlGroup = $('#' + field).parent().parent(); 59 | controlGroup.removeClass('error'); 60 | $('.help-inline', controlGroup).html(''); 61 | }, 62 | 63 | showAlert: function(title, text, klass) { 64 | $('.alert').removeClass("alert-error alert-warning alert-success alert-info"); 65 | $('.alert').addClass(klass); 66 | $('.alert').html('' + title + ' ' + text); 67 | $('.alert').show(); 68 | }, 69 | 70 | hideAlert: function() { 71 | $('.alert').hide(); 72 | } 73 | 74 | }; -------------------------------------------------------------------------------- /bootstrap/js/views/about.js: -------------------------------------------------------------------------------- 1 | window.AboutView = Backbone.View.extend({ 2 | 3 | initialize:function () { 4 | this.render(); 5 | }, 6 | 7 | render:function () { 8 | $(this.el).html(this.template()); 9 | return this; 10 | } 11 | 12 | }); -------------------------------------------------------------------------------- /bootstrap/js/views/header.js: -------------------------------------------------------------------------------- 1 | window.HeaderView = Backbone.View.extend({ 2 | 3 | initialize: function () { 4 | this.render(); 5 | }, 6 | 7 | render: function () { 8 | $(this.el).html(this.template()); 9 | return this; 10 | }, 11 | 12 | selectMenuItem: function (menuItem) { 13 | $('.nav li').removeClass('active'); 14 | if (menuItem) { 15 | $('.' + menuItem).addClass('active'); 16 | } 17 | } 18 | 19 | }); -------------------------------------------------------------------------------- /bootstrap/js/views/paginator.js: -------------------------------------------------------------------------------- 1 | window.Paginator = Backbone.View.extend({ 2 | 3 | className: "pagination pagination-centered", 4 | 5 | initialize:function () { 6 | this.model.bind("reset", this.render, this); 7 | this.render(); 8 | }, 9 | 10 | render:function () { 11 | 12 | var items = this.model.models; 13 | var len = items.length; 14 | var pageCount = Math.ceil(len / 8); 15 | 16 | $(this.el).html('
    '); 17 | 18 | for (var i=0; i < pageCount; i++) { 19 | $('ul', this.el).append("" + (i+1) + ""); 20 | } 21 | 22 | return this; 23 | } 24 | }); -------------------------------------------------------------------------------- /bootstrap/js/views/winedetails.js: -------------------------------------------------------------------------------- 1 | window.WineView = Backbone.View.extend({ 2 | 3 | initialize: function () { 4 | this.render(); 5 | }, 6 | 7 | render: function () { 8 | $(this.el).html(this.template(this.model.toJSON())); 9 | return this; 10 | }, 11 | 12 | events: { 13 | "change" : "change", 14 | "click .save" : "beforeSave", 15 | "click .delete" : "deleteWine", 16 | "drop #picture" : "dropHandler" 17 | }, 18 | 19 | change: function (event) { 20 | // Remove any existing alert message 21 | utils.hideAlert(); 22 | 23 | // Apply the change to the model 24 | var target = event.target; 25 | var change = {}; 26 | change[target.name] = target.value; 27 | this.model.set(change); 28 | 29 | // Run validation rule (if any) on changed item 30 | var check = this.model.validateItem(target.id); 31 | if (check.isValid === false) { 32 | utils.addValidationError(target.id, check.message); 33 | } else { 34 | utils.removeValidationError(target.id); 35 | } 36 | }, 37 | 38 | beforeSave: function () { 39 | var self = this; 40 | var check = this.model.validateAll(); 41 | if (check.isValid === false) { 42 | utils.displayValidationErrors(check.messages); 43 | return false; 44 | } 45 | // Upload picture file if a new file was dropped in the drop area 46 | if (this.pictureFile) { 47 | this.model.set("picture", this.pictureFile.name); 48 | utils.uploadFile(this.pictureFile, 49 | function () { 50 | self.saveWine(); 51 | } 52 | ); 53 | } else { 54 | this.saveWine(); 55 | } 56 | return false; 57 | }, 58 | 59 | saveWine: function () { 60 | var self = this; 61 | this.model.save(null, { 62 | success: function (model) { 63 | self.render(); 64 | app.navigate('wines/' + model.id, false); 65 | utils.showAlert('Success!', 'Wine saved successfully', 'alert-success'); 66 | }, 67 | error: function () { 68 | utils.showAlert('Error', 'An error occurred while trying to delete this item', 'alert-error'); 69 | } 70 | }); 71 | }, 72 | 73 | deleteWine: function () { 74 | this.model.destroy({ 75 | success: function () { 76 | alert('Wine deleted successfully'); 77 | window.history.back(); 78 | } 79 | }); 80 | return false; 81 | }, 82 | 83 | dropHandler: function (event) { 84 | event.stopPropagation(); 85 | event.preventDefault(); 86 | var e = event.originalEvent; 87 | e.dataTransfer.dropEffect = 'copy'; 88 | this.pictureFile = e.dataTransfer.files[0]; 89 | 90 | // Read the image file from the local file system and display it in the img tag 91 | var reader = new FileReader(); 92 | reader.onloadend = function () { 93 | $('#picture').attr('src', reader.result); 94 | }; 95 | reader.readAsDataURL(this.pictureFile); 96 | } 97 | 98 | }); -------------------------------------------------------------------------------- /bootstrap/js/views/winelist.js: -------------------------------------------------------------------------------- 1 | window.WineListView = Backbone.View.extend({ 2 | 3 | initialize: function () { 4 | this.render(); 5 | }, 6 | 7 | render: function () { 8 | var wines = this.model.models; 9 | var len = wines.length; 10 | var startPos = (this.options.page - 1) * 8; 11 | var endPos = Math.min(startPos + 8, len); 12 | 13 | $(this.el).html('
      '); 14 | 15 | for (var i = startPos; i < endPos; i++) { 16 | $('.thumbnails', this.el).append(new WineListItemView({model: wines[i]}).render().el); 17 | } 18 | 19 | $(this.el).append(new Paginator({model: this.model, page: this.options.page}).render().el); 20 | 21 | return this; 22 | } 23 | }); 24 | 25 | window.WineListItemView = Backbone.View.extend({ 26 | 27 | tagName: "li", 28 | 29 | className: "span3", 30 | 31 | initialize: function () { 32 | this.model.bind("change", this.render, this); 33 | this.model.bind("destroy", this.close, this); 34 | }, 35 | 36 | render: function () { 37 | $(this.el).html(this.template(this.model.toJSON())); 38 | return this; 39 | } 40 | 41 | }); -------------------------------------------------------------------------------- /bootstrap/lib/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* DROPDOWN CLASS DEFINITION 27 | * ========================= */ 28 | 29 | var toggle = '[data-toggle="dropdown"]' 30 | , Dropdown = function (element) { 31 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 32 | $('html').on('click.dropdown.data-api', function () { 33 | $el.parent().removeClass('open') 34 | }) 35 | } 36 | 37 | Dropdown.prototype = { 38 | 39 | constructor: Dropdown 40 | 41 | , toggle: function (e) { 42 | var $this = $(this) 43 | , $parent 44 | , selector 45 | , isActive 46 | 47 | if ($this.is('.disabled, :disabled')) return 48 | 49 | selector = $this.attr('data-target') 50 | 51 | if (!selector) { 52 | selector = $this.attr('href') 53 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 54 | } 55 | 56 | $parent = $(selector) 57 | $parent.length || ($parent = $this.parent()) 58 | 59 | isActive = $parent.hasClass('open') 60 | 61 | clearMenus() 62 | 63 | if (!isActive) $parent.toggleClass('open') 64 | 65 | return false 66 | } 67 | 68 | } 69 | 70 | function clearMenus() { 71 | $(toggle).parent().removeClass('open') 72 | } 73 | 74 | 75 | /* DROPDOWN PLUGIN DEFINITION 76 | * ========================== */ 77 | 78 | $.fn.dropdown = function (option) { 79 | return this.each(function () { 80 | var $this = $(this) 81 | , data = $this.data('dropdown') 82 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 83 | if (typeof option == 'string') data[option].call($this) 84 | }) 85 | } 86 | 87 | $.fn.dropdown.Constructor = Dropdown 88 | 89 | 90 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 91 | * =================================== */ 92 | 93 | $(function () { 94 | $('html').on('click.dropdown.data-api', clearMenus) 95 | $('body') 96 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() }) 97 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 98 | }) 99 | 100 | }(window.jQuery); -------------------------------------------------------------------------------- /bootstrap/pics/argiano.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/argiano.jpg -------------------------------------------------------------------------------- /bootstrap/pics/block_nine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/block_nine.jpg -------------------------------------------------------------------------------- /bootstrap/pics/bodega_lurton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/bodega_lurton.jpg -------------------------------------------------------------------------------- /bootstrap/pics/bouscat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/bouscat.jpg -------------------------------------------------------------------------------- /bootstrap/pics/calera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/calera.jpg -------------------------------------------------------------------------------- /bootstrap/pics/capineto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/capineto.png -------------------------------------------------------------------------------- /bootstrap/pics/caronne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/caronne.jpg -------------------------------------------------------------------------------- /bootstrap/pics/dinastia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/dinastia.jpg -------------------------------------------------------------------------------- /bootstrap/pics/domaine_serene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/domaine_serene.jpg -------------------------------------------------------------------------------- /bootstrap/pics/ex_umbris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/ex_umbris.jpg -------------------------------------------------------------------------------- /bootstrap/pics/fourvines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/fourvines.jpg -------------------------------------------------------------------------------- /bootstrap/pics/generic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/generic.jpg -------------------------------------------------------------------------------- /bootstrap/pics/hugel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/hugel.jpg -------------------------------------------------------------------------------- /bootstrap/pics/lan_rioja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/lan_rioja.jpg -------------------------------------------------------------------------------- /bootstrap/pics/le_doyenne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/le_doyenne.jpg -------------------------------------------------------------------------------- /bootstrap/pics/margerum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/margerum.jpg -------------------------------------------------------------------------------- /bootstrap/pics/momo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/momo.jpg -------------------------------------------------------------------------------- /bootstrap/pics/morizottes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/morizottes.jpg -------------------------------------------------------------------------------- /bootstrap/pics/petalos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/petalos.jpg -------------------------------------------------------------------------------- /bootstrap/pics/ponzi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/ponzi.jpg -------------------------------------------------------------------------------- /bootstrap/pics/quivira.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/quivira.jpg -------------------------------------------------------------------------------- /bootstrap/pics/rex_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/rex_hill.jpg -------------------------------------------------------------------------------- /bootstrap/pics/saint_cosme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/saint_cosme.jpg -------------------------------------------------------------------------------- /bootstrap/pics/shafer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/shafer.jpg -------------------------------------------------------------------------------- /bootstrap/pics/viticcio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/viticcio.jpg -------------------------------------------------------------------------------- /bootstrap/pics/waterbrook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/bootstrap/pics/waterbrook.jpg -------------------------------------------------------------------------------- /bootstrap/readme.md: -------------------------------------------------------------------------------- 1 | # Wine Cellar Sample Application with Backbone.js and Twitter Bootstrap # 2 | 3 | "Backbone Cellar" is a sample CRUD application built with Backbone.js and Twitter Bootstrap. The application allows you to browse through a list of wines, as well as add, update, and delete wines. 4 | 5 | This application is further documented [here](http://coenraets.org/blog). 6 | 7 | The application is also hosted online. You can test it [here](http://coenraets.org/backbone-cellar/bootstrap). 8 | 9 | The application is provided with both an in-memory datastore (default) and a RESTful back-end implemented in PHP (see the /api directory). 10 | 11 | The in-memory datastore allows you to experience the app without setting up a back-end. All the changes you make to the data will be lost the next time you access the app or hit Refresh. 12 | To use the app with the persistent RESTful back-end, set up the database as documented below and comment out the memorystore.js script import in index.html. 13 | 14 | ## Database Set Up (Optional): ## 15 | 16 | 1. Create a MySQL database name "cellar". 17 | 2. Execute cellar.sql to create and populate the "wine" table: 18 | 19 | mysql cellar -uroot < cellar.sql 20 | 21 | Your feedback is appreciated. Please post your questions and comments in the blog post referenced above. -------------------------------------------------------------------------------- /bootstrap/tpl/AboutView.html: -------------------------------------------------------------------------------- 1 |
      2 |
      3 | 4 |

      Download the source code

      5 |

      The source code for this application is available in this repository on GitHub.

      6 |
      7 |
      8 | 9 |
      10 | 11 |
      12 |
      13 | 14 | 15 |

      Comments and questions

      16 | 17 |

      I love to hear your feedback. Post your questions and comments on the blog post associated with this 18 | application.

      19 |
      20 |
      21 | 22 |
      23 | 24 | 25 |
      26 |
      27 | 28 | 29 |

      Follow me on Twitter

      30 | 31 |

      @ccoenraets

      32 |
      33 |
      34 | 35 |
      36 | 37 | 38 |
      39 |
      40 | 41 | 42 |

      Check out my blog

      43 | 44 |

      http://coenraets.org

      45 |
      46 |
      -------------------------------------------------------------------------------- /bootstrap/tpl/HeaderView.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/tpl/WineListItemView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
      <%= name %>
      4 |

      <%= year %> <%= grapes %>
      5 | <%= region %>, <%= country %>

      6 |
      7 | -------------------------------------------------------------------------------- /bootstrap/tpl/WineView.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 |
      4 | 5 | 6 |
      7 | 8 | Wine Details 9 |
      10 | 11 |
      12 |
      13 | 14 |
      15 | 16 | 17 |
      18 | 20 |
      21 |
      22 | 23 |
      24 | 25 | 26 |
      27 | 28 | 29 |
      30 |
      31 | 32 |
      33 | 34 | 35 |
      36 | 37 | 38 |
      39 |
      40 | 41 |
      42 | 43 | 44 |
      45 | 46 | 47 |
      48 |
      49 | 50 |
      51 | 52 | 53 |
      54 | 55 |
      56 |
      57 | 58 |
      59 | 60 | 61 |
      62 | 76 |
      77 |
      78 | 79 |
      80 | 81 | 82 |
      83 | 85 |
      86 |
      87 | 88 |
      89 | 90 |
      91 |
      92 |

      93 | 94 |

      To change the picture, drag a new picture from your file system onto the box above.

      95 |
      96 |
      97 | 98 |
      99 | 100 |
      101 | 102 |
      103 | Save 104 | Delete 105 |
      106 | 107 |
      108 | 109 |
      110 | 111 |
      112 |
      113 | 116 |
      117 |
      118 | 119 | 120 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Wine Cellar Sample Application # 2 | 3 | "Backbone Cellar" is a sample application built with [Backbone.js](http://documentcloud.github.com/backbone/). 4 | 5 | The application allows you to browse through a list of wines, add, update, and delete wines. 6 | 7 | This repository provides several implementations of the same application: 8 | 9 | 1. [Backbone.js + Twitter Bootstrap](https://github.com/ccoenraets/backbone-cellar/tree/master/bootstrap) implementation 10 | 2. [Backbone.js Tutorial](https://github.com/ccoenraets/backbone-cellar/tree/master/tutorial): A four-part Backbone.js tutorial 11 | -------------------------------------------------------------------------------- /tutorial/api/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | # Some hosts may require you to use the `RewriteBase` directive. 4 | # If you need to use the `RewriteBase` directive, it should be the 5 | # absolute physical path to the directory that contains this htaccess file. 6 | # 7 | # RewriteBase / 8 | 9 | RewriteCond %{REQUEST_FILENAME} !-f 10 | RewriteRule ^(.*)$ index.php [QSA,L] -------------------------------------------------------------------------------- /tutorial/api/Slim/Exception/Pass.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Pass Exception 35 | * 36 | * This Exception will cause the Router::dispatch method 37 | * to skip the current matching route and continue to the next 38 | * matching route. If no subsequent routes are found, a 39 | * HTTP 404 Not Found response will be sent to the client. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Exception_Pass extends Exception {} -------------------------------------------------------------------------------- /tutorial/api/Slim/Exception/RequestSlash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request Slash Exception 35 | * 36 | * This Exception is thrown when Slim detects a matching route 37 | * (defined with a trailing slash) and the HTTP request 38 | * matches the route but does not have a trailing slash. This 39 | * exception will be caught in `Slim::run` and trigger a 301 redirect 40 | * to the same resource URI with a trailing slash. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_Exception_RequestSlash extends Exception {} -------------------------------------------------------------------------------- /tutorial/api/Slim/Exception/Stop.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Stop Exception 35 | * 36 | * This Exception is thrown when the Slim application needs to abort 37 | * processing and return control flow to the outer PHP script. 38 | * 39 | * @package Slim 40 | * @author Josh Lockhart 41 | * @since Version 1.0 42 | */ 43 | class Slim_Exception_Stop extends Exception {} -------------------------------------------------------------------------------- /tutorial/api/Slim/Http/Uri.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Uri 35 | * 36 | * Parses base uri and application uri from Request. 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Uri { 43 | 44 | /** 45 | * @var string "https" or "http" 46 | */ 47 | protected static $scheme; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected static $baseUri; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected static $uri; 58 | 59 | /** 60 | * @var string The URI query string, excluding leading "?" 61 | */ 62 | protected static $queryString; 63 | 64 | /** 65 | * Get Base URI without trailing slash 66 | * @param bool $reload Force reparse the base URI? 67 | * @return string 68 | */ 69 | public static function getBaseUri( $reload = false ) { 70 | if ( $reload || is_null(self::$baseUri) ) { 71 | $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; //Full Request URI 72 | $scriptName = $_SERVER['SCRIPT_NAME']; //Script path from docroot 73 | $baseUri = strpos($requestUri, $scriptName) === 0 ? $scriptName : str_replace('\\', '/', dirname($scriptName)); 74 | self::$baseUri = rtrim($baseUri, '/'); 75 | } 76 | return self::$baseUri; 77 | } 78 | 79 | /** 80 | * Get URI with leading slash 81 | * @param bool $reload Force reparse the URI? 82 | * @return string 83 | * @throws RuntimeException If unable if unable to determine URI 84 | */ 85 | public static function getUri( $reload = false ) { 86 | if ( $reload || is_null(self::$uri) ) { 87 | $uri = ''; 88 | if ( !empty($_SERVER['PATH_INFO']) ) { 89 | $uri = $_SERVER['PATH_INFO']; 90 | } else { 91 | if ( isset($_SERVER['REQUEST_URI']) ) { 92 | $uri = parse_url(self::getScheme() . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); 93 | } else if ( isset($_SERVER['PHP_SELF']) ) { 94 | $uri = $_SERVER['PHP_SELF']; 95 | } else { 96 | throw new RuntimeException('Unable to detect request URI'); 97 | } 98 | } 99 | if ( self::getBaseUri() !== '' && strpos($uri, self::getBaseUri()) === 0 ) { 100 | $uri = substr($uri, strlen(self::getBaseUri())); 101 | } 102 | self::$uri = '/' . ltrim($uri, '/'); 103 | } 104 | return self::$uri; 105 | } 106 | 107 | /** 108 | * Get URI Scheme 109 | * @param bool $reload For reparse the URL scheme? 110 | * @return string "https" or "http" 111 | */ 112 | public static function getScheme( $reload = false ) { 113 | if ( $reload || is_null(self::$scheme) ) { 114 | self::$scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https'; 115 | } 116 | return self::$scheme; 117 | } 118 | 119 | /** 120 | * Get URI Query String 121 | * @param bool $reload For reparse the URL query string? 122 | * @return string 123 | */ 124 | public static function getQueryString( $reload = false ) { 125 | if ( $reload || is_null(self::$queryString) ) { 126 | self::$queryString = $_SERVER['QUERY_STRING']; 127 | } 128 | return self::$queryString; 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /tutorial/api/Slim/Log.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Log Adapter 35 | * 36 | * This is an adapter for your own custom Logger. This adapter assumes 37 | * your custom Logger provides the following public instance methods: 38 | * 39 | * debug( mixed $object ) 40 | * info( mixed $object ) 41 | * warn( mixed $object ) 42 | * error( mixed $object ) 43 | * fatal( mixed $object ) 44 | * 45 | * This class assumes nothing else about your custom Logger, so you are free 46 | * to use Apache's Log4PHP logger or any other log class that, at the 47 | * very least, implements the five public instance methods shown above. 48 | * 49 | * @package Slim 50 | * @author Josh Lockhart 51 | * @since Version 1.0 52 | */ 53 | class Slim_Log { 54 | 55 | /** 56 | * @var mixed An object that implements expected Logger interface 57 | */ 58 | protected $logger; 59 | 60 | /** 61 | * @var bool Enable logging? 62 | */ 63 | protected $enabled; 64 | 65 | /** 66 | * Constructor 67 | */ 68 | public function __construct() { 69 | $this->enabled = true; 70 | } 71 | 72 | /** 73 | * Enable or disable logging 74 | * @param bool $enabled 75 | * @return void 76 | */ 77 | public function setEnabled( $enabled ) { 78 | if ( $enabled ) { 79 | $this->enabled = true; 80 | } else { 81 | $this->enabled = false; 82 | } 83 | } 84 | 85 | /** 86 | * Is logging enabled? 87 | * @return bool 88 | */ 89 | public function isEnabled() { 90 | return $this->enabled; 91 | } 92 | 93 | /** 94 | * Log debug message 95 | * @param mixed $object 96 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 97 | */ 98 | public function debug( $object ) { 99 | return isset($this->logger) && $this->isEnabled() ? $this->logger->debug($object) : false; 100 | } 101 | 102 | /** 103 | * Log info message 104 | * @param mixed $object 105 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 106 | */ 107 | public function info( $object ) { 108 | return isset($this->logger) && $this->isEnabled() ? $this->logger->info($object) : false; 109 | } 110 | 111 | /** 112 | * Log warn message 113 | * @param mixed $object 114 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 115 | */ 116 | public function warn( $object ) { 117 | return isset($this->logger) && $this->isEnabled() ? $this->logger->warn($object) : false; 118 | } 119 | 120 | /** 121 | * Log error message 122 | * @param mixed $object 123 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 124 | */ 125 | public function error( $object ) { 126 | return isset($this->logger) && $this->isEnabled() ? $this->logger->error($object) : false; 127 | } 128 | 129 | /** 130 | * Log fatal message 131 | * @param mixed $object 132 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 133 | */ 134 | public function fatal( $object ) { 135 | return isset($this->logger) && $this->isEnabled() ? $this->logger->fatal($object) : false; 136 | } 137 | 138 | /** 139 | * Set Logger 140 | * @param mixed $logger 141 | * @return void 142 | */ 143 | public function setLogger( $logger ) { 144 | $this->logger = $logger; 145 | } 146 | 147 | /** 148 | * Get Logger 149 | * @return mixed 150 | */ 151 | public function getLogger() { 152 | return $this->logger; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /tutorial/api/Slim/Session/Handler.php: -------------------------------------------------------------------------------- 1 | app = $app; 58 | return session_set_save_handler( 59 | array($this, 'open'), 60 | array($this, 'close'), 61 | array($this, 'read'), 62 | array($this, 'write'), 63 | array($this, 'destroy'), 64 | array($this, 'gc') 65 | ); 66 | } 67 | 68 | /** 69 | * Open session 70 | * 71 | * @param string $savePath 72 | * @param string $sessionName 73 | * @return mixed 74 | */ 75 | abstract public function open( $savePath, $sessionName ); 76 | 77 | /** 78 | * Close session 79 | * 80 | * @return mixed 81 | */ 82 | abstract public function close(); 83 | 84 | /** 85 | * Read session data with ID 86 | * 87 | * @param string $id The session identifier 88 | * @return string 89 | */ 90 | abstract public function read( $id ); 91 | 92 | /** 93 | * Write session data with ID 94 | * 95 | * The "write" handler is not executed until after the output stream is 96 | * closed. Thus, output from debugging statements in the "write" handler 97 | * will never be seen in the browser. If debugging output is necessary, it 98 | * is suggested that the debug output be written to a file instead. 99 | * 100 | * @param string $id The session identifier 101 | * @param mixed $sessionData The session data 102 | * @return mixed 103 | */ 104 | abstract public function write( $id, $sessionData ); 105 | 106 | /** 107 | * Destroy session with ID 108 | * 109 | * @param string $id The session identifier 110 | * @return mixed 111 | */ 112 | abstract public function destroy( $id ); 113 | 114 | /** 115 | * Session garbage collection 116 | * 117 | * Executed when the PHP session garbage collector is invoked; should 118 | * remove all session data older than the `$maxLifetime`. 119 | * 120 | * @param int $maxLifetime 121 | * @return mixed 122 | */ 123 | abstract public function gc( $maxLifetime ); 124 | 125 | } -------------------------------------------------------------------------------- /tutorial/api/Slim/Session/Handler/Cookies.php: -------------------------------------------------------------------------------- 1 | app->getEncryptedCookie($id); 57 | } 58 | 59 | public function write( $id, $sessionData ) { 60 | $this->app->setEncryptedCookie($id, $sessionData, 0); 61 | } 62 | 63 | public function destroy( $id ) { 64 | $this->app->deleteCookie($id); 65 | } 66 | 67 | public function gc( $maxLifetime ) { 68 | return true; //Not used 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /tutorial/api/index.php: -------------------------------------------------------------------------------- 1 | get('/wines', 'getWines'); 8 | $app->get('/wines/:id', 'getWine'); 9 | $app->get('/wines/search/:query', 'findByName'); 10 | $app->post('/wines', 'addWine'); 11 | $app->put('/wines/:id', 'updateWine'); 12 | $app->delete('/wines/:id', 'deleteWine'); 13 | 14 | $app->run(); 15 | 16 | function getWines() { 17 | $sql = "select * FROM wine ORDER BY name"; 18 | try { 19 | $db = getConnection(); 20 | $stmt = $db->query($sql); 21 | $wines = $stmt->fetchAll(PDO::FETCH_OBJ); 22 | $db = null; 23 | // echo '{"wine": ' . json_encode($wines) . '}'; 24 | echo json_encode($wines); 25 | } catch(PDOException $e) { 26 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 27 | } 28 | } 29 | 30 | function getWine($id) { 31 | $sql = "SELECT * FROM wine WHERE id=:id"; 32 | try { 33 | $db = getConnection(); 34 | $stmt = $db->prepare($sql); 35 | $stmt->bindParam("id", $id); 36 | $stmt->execute(); 37 | $wine = $stmt->fetchObject(); 38 | $db = null; 39 | echo json_encode($wine); 40 | } catch(PDOException $e) { 41 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 42 | } 43 | } 44 | 45 | function addWine() { 46 | error_log('addWine\n', 3, '/var/tmp/php.log'); 47 | $request = Slim::getInstance()->request(); 48 | $wine = json_decode($request->getBody()); 49 | $sql = "INSERT INTO wine (name, grapes, country, region, year, description) VALUES (:name, :grapes, :country, :region, :year, :description)"; 50 | try { 51 | $db = getConnection(); 52 | $stmt = $db->prepare($sql); 53 | $stmt->bindParam("name", $wine->name); 54 | $stmt->bindParam("grapes", $wine->grapes); 55 | $stmt->bindParam("country", $wine->country); 56 | $stmt->bindParam("region", $wine->region); 57 | $stmt->bindParam("year", $wine->year); 58 | $stmt->bindParam("description", $wine->description); 59 | $stmt->execute(); 60 | $wine->id = $db->lastInsertId(); 61 | $db = null; 62 | echo json_encode($wine); 63 | } catch(PDOException $e) { 64 | error_log($e->getMessage(), 3, '/var/tmp/php.log'); 65 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 66 | } 67 | } 68 | 69 | function updateWine($id) { 70 | $request = Slim::getInstance()->request(); 71 | $body = $request->getBody(); 72 | $wine = json_decode($body); 73 | $sql = "UPDATE wine SET name=:name, grapes=:grapes, country=:country, region=:region, year=:year, description=:description WHERE id=:id"; 74 | try { 75 | $db = getConnection(); 76 | $stmt = $db->prepare($sql); 77 | $stmt->bindParam("name", $wine->name); 78 | $stmt->bindParam("grapes", $wine->grapes); 79 | $stmt->bindParam("country", $wine->country); 80 | $stmt->bindParam("region", $wine->region); 81 | $stmt->bindParam("year", $wine->year); 82 | $stmt->bindParam("description", $wine->description); 83 | $stmt->bindParam("id", $id); 84 | $stmt->execute(); 85 | $db = null; 86 | echo json_encode($wine); 87 | } catch(PDOException $e) { 88 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 89 | } 90 | } 91 | 92 | function deleteWine($id) { 93 | $sql = "DELETE FROM wine WHERE id=:id"; 94 | try { 95 | $db = getConnection(); 96 | $stmt = $db->prepare($sql); 97 | $stmt->bindParam("id", $id); 98 | $stmt->execute(); 99 | $db = null; 100 | } catch(PDOException $e) { 101 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 102 | } 103 | } 104 | 105 | function findByName($query) { 106 | $sql = "SELECT * FROM wine WHERE UPPER(name) LIKE :query ORDER BY name"; 107 | try { 108 | $db = getConnection(); 109 | $stmt = $db->prepare($sql); 110 | $query = "%".$query."%"; 111 | $stmt->bindParam("query", $query); 112 | $stmt->execute(); 113 | $wines = $stmt->fetchAll(PDO::FETCH_OBJ); 114 | $db = null; 115 | echo json_encode($wines); 116 | } catch(PDOException $e) { 117 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 118 | } 119 | } 120 | 121 | function getConnection() { 122 | $dbhost="127.0.0.1"; 123 | $dbuser="root"; 124 | $dbpass=""; 125 | $dbname="cellar"; 126 | $dbh = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass); 127 | $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 128 | return $dbh; 129 | } 130 | 131 | ?> -------------------------------------------------------------------------------- /tutorial/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 18px; 4 | } 5 | 6 | #header { 7 | padding-top: 5px; 8 | } 9 | 10 | #sidebar { 11 | position: absolute; 12 | left: 10px; 13 | top: 70px; 14 | bottom: 20px; 15 | width: 260px; 16 | border: solid 1px #CCCCCC; 17 | overflow-y: scroll; 18 | } 19 | 20 | #content { 21 | position: absolute; 22 | top: 70px; 23 | bottom: 20px; 24 | left: 300px; 25 | right: 20px; 26 | } 27 | 28 | .form-left-col { 29 | width: 310px; 30 | float: left; 31 | } 32 | 33 | .form-right-col { 34 | } 35 | 36 | .title { 37 | font-size: 20px; 38 | font-weight: bold; 39 | } 40 | 41 | ul { 42 | list-style-type: none; 43 | padding-left: 0px; 44 | margin-top: 0px; 45 | } 46 | 47 | li a { 48 | text-decoration: none; 49 | display: block; 50 | color: #000000; 51 | border-bottom: solid 1px #CCCCCC; 52 | padding: 8px; 53 | } 54 | 55 | li a:hover { 56 | background-color: #4B0A1E; 57 | color: #BA8A92; 58 | } 59 | 60 | input, textarea { 61 | border: 1px solid #ccc; 62 | min-height: 30px; 63 | outline: none; 64 | } 65 | 66 | .form-left-col input { 67 | margin-bottom: 15px; 68 | margin-top: 5px; 69 | width: 280px; 70 | } 71 | 72 | textarea { 73 | margin-bottom: 15px; 74 | margin-top: 5px; 75 | height: 200px; 76 | width: 250px; 77 | } 78 | 79 | label { 80 | display: block; 81 | } 82 | 83 | button { 84 | padding: 6px; 85 | } -------------------------------------------------------------------------------- /tutorial/final/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Backbone Cellar 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
      15 |

      Welcome to Backbone Cellar

      16 |

      17 | This is a sample application part of of three-part tutorial showing how to build a CRUD application with Backbone.js. 18 |

      19 |
      20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tutorial/final/js/main.js: -------------------------------------------------------------------------------- 1 | Backbone.View.prototype.close = function () { 2 | console.log('Closing view ' + this); 3 | if (this.beforeClose) { 4 | this.beforeClose(); 5 | } 6 | this.remove(); 7 | this.unbind(); 8 | }; 9 | 10 | var AppRouter = Backbone.Router.extend({ 11 | 12 | initialize:function () { 13 | $('#header').html(new HeaderView().render().el); 14 | }, 15 | 16 | routes:{ 17 | "":"list", 18 | "wines/new":"newWine", 19 | "wines/:id":"wineDetails" 20 | }, 21 | 22 | list:function () { 23 | this.before(); 24 | }, 25 | 26 | wineDetails:function (id) { 27 | this.before(function () { 28 | var wine = app.wineList.get(id); 29 | app.showView('#content', new WineView({model:wine})); 30 | }); 31 | }, 32 | 33 | newWine:function () { 34 | this.before(function () { 35 | app.showView('#content', new WineView({model:new Wine()})); 36 | }); 37 | }, 38 | 39 | showView:function (selector, view) { 40 | if (this.currentView) 41 | this.currentView.close(); 42 | $(selector).html(view.render().el); 43 | this.currentView = view; 44 | return view; 45 | }, 46 | 47 | before:function (callback) { 48 | if (this.wineList) { 49 | if (callback) callback(); 50 | } else { 51 | this.wineList = new WineCollection(); 52 | this.wineList.fetch({success:function () { 53 | $('#sidebar').html(new WineListView({model:app.wineList}).render().el); 54 | if (callback) callback(); 55 | }}); 56 | } 57 | } 58 | 59 | }); 60 | 61 | tpl.loadTemplates(['header', 'wine-details', 'wine-list-item'], function () { 62 | app = new AppRouter(); 63 | Backbone.history.start(); 64 | }); -------------------------------------------------------------------------------- /tutorial/final/js/models/winemodel.js: -------------------------------------------------------------------------------- 1 | window.Wine = Backbone.Model.extend({ 2 | urlRoot:"../api/wines", 3 | defaults:{ 4 | "id":null, 5 | "name":"", 6 | "grapes":"", 7 | "country":"USA", 8 | "region":"California", 9 | "year":"", 10 | "description":"", 11 | "picture":"" 12 | } 13 | }); 14 | 15 | window.WineCollection = Backbone.Collection.extend({ 16 | model:Wine, 17 | url:"../api/wines" 18 | }); -------------------------------------------------------------------------------- /tutorial/final/js/utils.js: -------------------------------------------------------------------------------- 1 | tpl = { 2 | 3 | // Hash of preloaded templates for the app 4 | templates:{}, 5 | 6 | // Recursively pre-load all the templates for the app. 7 | // This implementation should be changed in a production environment. All the template files should be 8 | // concatenated in a single file. 9 | loadTemplates:function (names, callback) { 10 | 11 | var that = this; 12 | 13 | var loadTemplate = function (index) { 14 | var name = names[index]; 15 | console.log('Loading template: ' + name); 16 | $.get('tpl/' + name + '.html', function (data) { 17 | that.templates[name] = data; 18 | index++; 19 | if (index < names.length) { 20 | loadTemplate(index); 21 | } else { 22 | callback(); 23 | } 24 | }); 25 | } 26 | 27 | loadTemplate(0); 28 | }, 29 | 30 | // Get template by name from hash of preloaded templates 31 | get:function (name) { 32 | return this.templates[name]; 33 | } 34 | 35 | }; -------------------------------------------------------------------------------- /tutorial/final/js/views/header.js: -------------------------------------------------------------------------------- 1 | window.HeaderView = Backbone.View.extend({ 2 | 3 | initialize:function () { 4 | this.template = _.template(tpl.get('header')); 5 | }, 6 | 7 | render:function (eventName) { 8 | $(this.el).html(this.template()); 9 | return this; 10 | }, 11 | 12 | events:{ 13 | "click .new":"newWine" 14 | }, 15 | 16 | newWine:function (event) { 17 | app.navigate("wines/new", true); 18 | return false; 19 | } 20 | 21 | }); -------------------------------------------------------------------------------- /tutorial/final/js/views/winedetails.js: -------------------------------------------------------------------------------- 1 | window.WineView = Backbone.View.extend({ 2 | 3 | tagName:"div", // Not required since 'div' is the default if no el or tagName specified 4 | 5 | initialize:function () { 6 | 7 | this.template = _.template(tpl.get('wine-details')); 8 | this.model.bind("change", this.render, this); 9 | }, 10 | 11 | render:function (eventName) { 12 | $(this.el).html(this.template(this.model.toJSON())); 13 | return this; 14 | }, 15 | 16 | events:{ 17 | "change input":"change", 18 | "click .save":"saveWine", 19 | "click .delete":"deleteWine" 20 | }, 21 | 22 | change:function (event) { 23 | var target = event.target; 24 | console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value); 25 | // You could change your model on the spot, like this: 26 | // var change = {}; 27 | // change[target.name] = target.value; 28 | // this.model.set(change); 29 | }, 30 | 31 | saveWine:function () { 32 | this.model.set({ 33 | name:$('#name').val(), 34 | grapes:$('#grapes').val(), 35 | country:$('#country').val(), 36 | region:$('#region').val(), 37 | year:$('#year').val(), 38 | description:$('#description').val() 39 | }); 40 | if (this.model.isNew()) { 41 | var self = this; 42 | app.wineList.create(this.model, { 43 | success:function () { 44 | app.navigate('wines/' + self.model.id, false); 45 | } 46 | }); 47 | } else { 48 | this.model.save(); 49 | } 50 | 51 | return false; 52 | }, 53 | 54 | deleteWine:function () { 55 | this.model.destroy({ 56 | success:function () { 57 | alert('Wine deleted successfully'); 58 | window.history.back(); 59 | } 60 | }); 61 | return false; 62 | } 63 | 64 | }); -------------------------------------------------------------------------------- /tutorial/final/js/views/winelist.js: -------------------------------------------------------------------------------- 1 | window.WineListView = Backbone.View.extend({ 2 | 3 | tagName:'ul', 4 | 5 | initialize:function () { 6 | this.model.bind("reset", this.render, this); 7 | var self = this; 8 | this.model.bind("add", function (wine) { 9 | $(self.el).append(new WineListItemView({model:wine}).render().el); 10 | }); 11 | }, 12 | 13 | render:function (eventName) { 14 | _.each(this.model.models, function (wine) { 15 | $(this.el).append(new WineListItemView({model:wine}).render().el); 16 | }, this); 17 | return this; 18 | } 19 | }); 20 | 21 | window.WineListItemView = Backbone.View.extend({ 22 | 23 | tagName:"li", 24 | 25 | initialize:function () { 26 | this.template = _.template(tpl.get('wine-list-item')); 27 | this.model.bind("change", this.render, this); 28 | this.model.bind("destroy", this.close, this); 29 | }, 30 | 31 | render:function (eventName) { 32 | $(this.el).html(this.template(this.model.toJSON())); 33 | return this; 34 | } 35 | 36 | }); -------------------------------------------------------------------------------- /tutorial/final/tpl/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tutorial/final/tpl/wine-details.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
      23 | 24 |
      25 | 26 | 27 | 28 |
      29 | -------------------------------------------------------------------------------- /tutorial/final/tpl/wine-list-item.html: -------------------------------------------------------------------------------- 1 | <%= name %> -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | BackboneCellarAndroid 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Mon Feb 06 12:01:49 EST 2012 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.6 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=1.6 13 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 5 | } 6 | 7 | .header { 8 | position: absolute; 9 | text-align: center; 10 | background: #666666; /* for non-css3 browsers */ 11 | background: -webkit-gradient(linear, left top, left bottom, from(#4B0A1E), to(#4B0A1E), color-stop(.6,#BA8A92)); 12 | color: #FFFFFF; 13 | top: 0px; 14 | left: 0px; 15 | height: 44px; 16 | right: 0px; 17 | border-bottom: solid 1px #444444; 18 | } 19 | 20 | h1 { 21 | font-size: 20px; 22 | font-weight: bold; 23 | } 24 | 25 | h2 { 26 | font-size: 18px; 27 | font-weight: bold; 28 | } 29 | 30 | .header h2 { 31 | margin-top: 0px; 32 | padding-top: 13px; 33 | } 34 | 35 | .header .nav { 36 | padding-top: 14px; 37 | position: absolute; 38 | left: 12px; 39 | } 40 | 41 | .header > .action { 42 | position: absolute; 43 | right: 10px; 44 | } 45 | 46 | .header a { 47 | text-decoration:none; 48 | color: #FFFFFF; 49 | } 50 | 51 | .content { 52 | position: absolute; 53 | top: 44px; 54 | left: 0px; 55 | bottom: 0px; 56 | right: 0px; 57 | overflow-y: scroll; 58 | } 59 | 60 | .details { 61 | padding: 12px; 62 | } 63 | 64 | .content img { 65 | float: right; 66 | width: 100px; 67 | height: 200px; 68 | } 69 | 70 | ul { 71 | list-style-type: none; 72 | padding-left: 0px; 73 | margin-top: 0px; 74 | } 75 | 76 | li a { 77 | text-decoration:none; 78 | display: block; 79 | color: #000000; 80 | border-bottom:solid 1px #CCCCCC; 81 | padding: 8px; 82 | } 83 | 84 | li a:hover { 85 | background-color: #4B0A1E; 86 | color: #BA8A92; 87 | } 88 | 89 | button { 90 | padding:6px; 91 | } -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/js/main.js: -------------------------------------------------------------------------------- 1 | Backbone.View.prototype.close = function () { 2 | console.log('Closing view ' + this); 3 | if (this.beforeClose) { 4 | this.beforeClose(); 5 | } 6 | this.remove(); 7 | this.unbind(); 8 | }; 9 | 10 | var AppRouter = Backbone.Router.extend({ 11 | 12 | routes: { 13 | "" : "list", 14 | "wines/:id" : "wineDetails" 15 | }, 16 | 17 | list: function() { 18 | this.before(function() { 19 | app.showView( new WineListView({model: app.wineList}) ); 20 | }); 21 | }, 22 | 23 | wineDetails: function(id) { 24 | this.before(function() { 25 | var wine = app.wineList.get(id); 26 | app.showView( new WineView({model: wine}) ); 27 | }); 28 | }, 29 | 30 | showView: function(view) { 31 | if (this.currentView) 32 | this.currentView.close(); 33 | $('body').html(view.render().el); 34 | this.currentView = view; 35 | return view; 36 | }, 37 | 38 | before: function(callback) { 39 | if (this.wineList) { 40 | callback(); 41 | } else { 42 | this.wineList = new WineCollection(); 43 | this.wineList.fetch({success: function() { 44 | callback(); 45 | }}); 46 | } 47 | } 48 | 49 | 50 | }); 51 | 52 | function startApp() { 53 | tpl.loadTemplates(['wine-list', 'wine-details', 'wine-list-item'], function() { 54 | app = new AppRouter(); 55 | Backbone.history.start(); 56 | }); 57 | } -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/js/models/winemodel.js: -------------------------------------------------------------------------------- 1 | window.Wine = Backbone.Model.extend({ 2 | urlRoot: "http://coenraets.org/backbone-cellar/part1/api/wines", 3 | defaults: { 4 | "id": null, 5 | "name": "", 6 | "grapes": "", 7 | "country": "USA", 8 | "region": "California", 9 | "year": "", 10 | "description": "", 11 | "picture": "" 12 | } 13 | }); 14 | 15 | window.WineCollection = Backbone.Collection.extend({ 16 | model: Wine, 17 | url: "http://coenraets.org/backbone-cellar/part1/api/wines" 18 | }); -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/js/utils.js: -------------------------------------------------------------------------------- 1 | tpl = { 2 | 3 | // Map of preloaded templates for the app 4 | templates: {}, 5 | 6 | // Recursively pre-load all the templates for the app. 7 | // This implementation should be changed in a production environment. A build script should concatenate 8 | // all the template files in a single file. 9 | loadTemplates: function(names, callback) { 10 | 11 | var that = this; 12 | 13 | var loadTemplate = function(index) { 14 | var name = names[index]; 15 | console.log('Loading template: ' + name); 16 | $.get('tpl/' + name + '.html', function(data) { 17 | that.templates[name] = data; 18 | index++; 19 | if (index < names.length) { 20 | loadTemplate(index); 21 | } else { 22 | callback(); 23 | } 24 | }, 'text'); 25 | } 26 | 27 | loadTemplate(0); 28 | }, 29 | 30 | // Get template by name from map of preloaded templates 31 | get: function(name) { 32 | return this.templates[name]; 33 | } 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/js/views/winedetails.js: -------------------------------------------------------------------------------- 1 | window.WineView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | this.template = _.template(tpl.get('wine-details')); 5 | }, 6 | 7 | render: function(eventName) { 8 | $(this.el).html(this.template(this.model.toJSON())); 9 | return this; 10 | } 11 | 12 | }); -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/js/views/winelist.js: -------------------------------------------------------------------------------- 1 | window.WineListView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | this.template = _.template(tpl.get('wine-list')); 5 | this.model.bind("reset", this.render, this); 6 | this.model.bind("add", function(wine) { 7 | $(this.el).append(new WineListItemView({model: wine}).render().el); 8 | }); 9 | }, 10 | 11 | render: function(eventName) { 12 | $(this.el).html(this.template()); 13 | var ul = $('ul', $(this.el)); 14 | _.each(this.model.models, function(wine) { 15 | ul.append(new WineListItemView({model: wine}).render().el); 16 | }, this); 17 | return this; 18 | } 19 | }); 20 | 21 | window.WineListItemView = Backbone.View.extend({ 22 | 23 | tagName: "li", 24 | 25 | initialize: function() { 26 | this.template = _.template(tpl.get('wine-list-item')); 27 | this.model.bind("change", this.render, this); 28 | this.model.bind("destroy", this.close, this); 29 | }, 30 | 31 | render: function(eventName) { 32 | $(this.el).html(this.template(this.model.toJSON())); 33 | return this; 34 | } 35 | 36 | }); -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/block_nine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/block_nine.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/bodega_lurton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/bodega_lurton.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/bouscat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/bouscat.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/domaine_serene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/domaine_serene.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/ex_umbris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/ex_umbris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/generic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/generic.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/lan_rioja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/lan_rioja.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/le_doyenne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/le_doyenne.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/lurton-pinot-gris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/lurton-pinot-gris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/margerum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/margerum.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/morizottes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/morizottes.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/rex_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/rex_hill.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/saint_cosme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/saint_cosme.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/viticcio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/assets/www/pics/viticcio.jpg -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/tpl/wine-details.html: -------------------------------------------------------------------------------- 1 |
      2 | List 3 |

      Backbone Cellar

      4 |
      5 | 6 |
      7 |

      <%= name %>

      8 | <%= year %>
      9 | <%= grapes %>
      10 | <%= region %>, <%= country %> 11 |

      12 |

      13 | 14 | <%= description %> 15 |

      16 |
      17 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/tpl/wine-list-item.html: -------------------------------------------------------------------------------- 1 | <%= name %> -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/assets/www/tpl/wine-list.html: -------------------------------------------------------------------------------- 1 |
      2 |

      Backbone Cellar

      3 |
      4 | 5 |
      6 | 7 |
      8 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/BackboneCellarAndroid.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/BackboneCellarAndroid.apk -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/classes.dex -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/BackboneCellarAndroidActivity.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/BackboneCellarAndroidActivity.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$attr.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$attr.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$drawable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$drawable.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$layout.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$layout.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$string.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$string.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$xml.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R$xml.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/org/coenraets/cellar/R.class -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/bin/resources.ap_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/bin/resources.ap_ -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-8 12 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/gen/org/coenraets/cellar/R.java: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED FILE. DO NOT MODIFY. 2 | * 3 | * This class was automatically generated by the 4 | * aapt tool from the resource data it found. It 5 | * should not be modified by hand. 6 | */ 7 | 8 | package org.coenraets.cellar; 9 | 10 | public final class R { 11 | public static final class attr { 12 | } 13 | public static final class drawable { 14 | public static final int icon=0x7f020000; 15 | } 16 | public static final class layout { 17 | public static final int main=0x7f030000; 18 | } 19 | public static final class string { 20 | public static final int app_name=0x7f050001; 21 | public static final int hello=0x7f050000; 22 | } 23 | public static final class xml { 24 | public static final int phonegap=0x7f040000; 25 | public static final int plugins=0x7f040001; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/libs/phonegap-1.4.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/libs/phonegap-1.4.1.jar -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembers class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembers class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers class * extends android.app.Activity { 30 | public void *(android.view.View); 31 | } 32 | 33 | -keepclassmembers enum * { 34 | public static **[] values(); 35 | public static ** valueOf(java.lang.String); 36 | } 37 | 38 | -keep class * implements android.os.Parcelable { 39 | public static final android.os.Parcelable$Creator *; 40 | } 41 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/android/BackboneCellarAndroid/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World, BackboneCellarAndroidActivity! 4 | BackboneCellarAndroid 5 | 6 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/xml/phonegap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/res/xml/plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tutorial/mobile/android/BackboneCellarAndroid/src/org/coenraets/cellar/BackboneCellarAndroidActivity.java: -------------------------------------------------------------------------------- 1 | package org.coenraets.cellar; 2 | 3 | import com.phonegap.DroidGap; 4 | 5 | //import android.app.Activity; 6 | import android.os.Bundle; 7 | 8 | public class BackboneCellarAndroidActivity extends DroidGap { 9 | /** Called when the activity is first created. */ 10 | @Override 11 | public void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | // setContentView(R.layout.main); 14 | super.loadUrl("file:///android_asset/www/index.html"); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/project.xcworkspace/xcuserdata/christophe.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/project.xcworkspace/xcuserdata/christophe.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/xcuserdata/christophe.xcuserdatad/xcschemes/WineCellar 2.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/xcuserdata/christophe.xcuserdatad/xcschemes/WineCellar.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar.xcodeproj/xcuserdata/christophe.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WineCellar 2.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | WineCellar.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | D90E855A14DED10400F162D1 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Classes/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | // 21 | // AppDelegate.h 22 | // WineCellar 23 | // 24 | // Created by Christophe Coenraets on 2/5/12. 25 | // Copyright Adobe 2012. All rights reserved. 26 | // 27 | 28 | #import 29 | 30 | #ifdef PHONEGAP_FRAMEWORK 31 | #import 32 | #else 33 | #import "PGViewController.h" 34 | #endif 35 | 36 | 37 | @interface AppDelegate : NSObject < UIApplicationDelegate, UIWebViewDelegate, PGCommandDelegate > { 38 | 39 | NSString* invokeString; 40 | } 41 | 42 | // invoke string is passed to your app on launch, this is only valid if you 43 | // edit FooBar.plist to add a protocol 44 | // a simple tutorial can be found here : 45 | // http://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html 46 | 47 | @property (nonatomic, copy) NSString* invokeString; 48 | @property (nonatomic, retain) IBOutlet UIWindow* window; 49 | @property (nonatomic, retain) IBOutlet PGViewController* viewController; 50 | 51 | @end 52 | 53 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Classes/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // FooBar 4 | // 5 | // Created by Shazron Abdullah on 12-01-26. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #ifdef PHONEGAP_FRAMEWORK 10 | #import 11 | #else 12 | #import "PGViewController.h" 13 | #endif 14 | 15 | @interface MainViewController : PGViewController 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Classes/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.m 3 | // WineCellar 4 | // 5 | #import "MainViewController.h" 6 | 7 | @implementation MainViewController 8 | 9 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 10 | { 11 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 12 | if (self) { 13 | // Custom initialization 14 | } 15 | return self; 16 | } 17 | 18 | - (void)didReceiveMemoryWarning 19 | { 20 | // Releases the view if it doesn't have a superview. 21 | [super didReceiveMemoryWarning]; 22 | 23 | // Release any cached data, images, etc that aren't in use. 24 | } 25 | 26 | #pragma mark - View lifecycle 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | // Do any additional setup after loading the view from its nib. 32 | } 33 | 34 | - (void)viewDidUnload 35 | { 36 | [super viewDidUnload]; 37 | // Release any retained subviews of the main view. 38 | // e.g. self.myOutlet = nil; 39 | } 40 | 41 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 42 | { 43 | // Return YES for supported orientations 44 | return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Classes/MainViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C25 6 | 1919 7 | 1138.11 8 | 566.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 916 12 | 13 | 14 | IBProxyObject 15 | IBUIView 16 | 17 | 18 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 19 | 20 | 21 | PluginDependencyRecalculationVersion 22 | 23 | 24 | 25 | 26 | IBFilesOwner 27 | IBCocoaTouchFramework 28 | 29 | 30 | IBFirstResponder 31 | IBCocoaTouchFramework 32 | 33 | 34 | 35 | 274 36 | {{0, 20}, {320, 460}} 37 | 38 | 39 | 40 | 3 41 | MQA 42 | 43 | 2 44 | 45 | 46 | 47 | IBCocoaTouchFramework 48 | 49 | 50 | 51 | 52 | 53 | 54 | view 55 | 56 | 57 | 58 | 3 59 | 60 | 61 | 62 | 63 | 64 | 0 65 | 66 | 67 | 68 | 69 | 70 | 1 71 | 72 | 73 | 74 | 75 | -1 76 | 77 | 78 | File's Owner 79 | 80 | 81 | -2 82 | 83 | 84 | 85 | 86 | 87 | 88 | MainViewController 89 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 90 | UIResponder 91 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 92 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 93 | 94 | 95 | 96 | 97 | 98 | 3 99 | 100 | 101 | 102 | 103 | MainViewController 104 | UIViewController 105 | 106 | IBProjectSource 107 | ./Classes/MainViewController.h 108 | 109 | 110 | 111 | 112 | 0 113 | IBCocoaTouchFramework 114 | YES 115 | 3 116 | 916 117 | 118 | 119 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/PhoneGap.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TopActivityIndicator 6 | gray 7 | EnableLocation 8 | 9 | EnableViewportScale 10 | 11 | AutoHideSplashScreen 12 | 13 | ShowSplashScreenSpinner 14 | 15 | MediaPlaybackRequiresUserAction 16 | 17 | AllowInlineMediaPlayback 18 | 19 | OpenAllWhitelistURLsInWebView 20 | 21 | ExternalHosts 22 | 23 | coenraets.org 24 | 25 | Plugins 26 | 27 | com.phonegap.accelerometer 28 | PGAccelerometer 29 | com.phonegap.camera 30 | PGCamera 31 | com.phonegap.connection 32 | PGConnection 33 | com.phonegap.contacts 34 | PGContacts 35 | com.phonegap.debugconsole 36 | PGDebugConsole 37 | com.phonegap.file 38 | PGFile 39 | com.phonegap.filetransfer 40 | PGFileTransfer 41 | com.phonegap.geolocation 42 | PGLocation 43 | com.phonegap.notification 44 | PGNotification 45 | com.phonegap.media 46 | PGSound 47 | com.phonegap.mediacapture 48 | PGCapture 49 | com.phonegap.splashscreen 50 | PGSplashScreen 51 | com.phonegap.battery 52 | PGBattery 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Plugins/README: -------------------------------------------------------------------------------- 1 | Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder. -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/controls_bg~ipad.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/microphone~ipad.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/record_button~ipad.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/recording_bg~ipad.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/Capture.bundle/stop_button~ipad.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | // accessibility label for recording button 21 | "toggle audio recording" = "toggle audio recording"; 22 | // notification spoken by VoiceOver when timed recording finishes 23 | "timed recording complete" = "timed recording complete"; 24 | // accessibility hint for display of recorded elapsed time 25 | "recorded time in minutes and seconds" = "recorded time in minutes and seconds"; -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | // accessibility label for recording button 21 | "toggle audio recording" = "grabación de audio cambiar"; 22 | // notification spoken by VoiceOver when timed recording finishes 23 | "timed recording complete" = "tiempo de grabación completo"; 24 | // accessibility hint for display of recorded elapsed time 25 | "recorded time in minutes and seconds" = "tiempo registrado en minutos y segundos"; -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon-72.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/icons/icon@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/splash/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/splash/Default.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/Resources/splash/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/WineCellar/Resources/splash/Default@2x.png -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/WineCellar-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIconFiles 6 | 7 | icon.png 8 | icon@2x.png 9 | icon-72.png 10 | 11 | UISupportedInterfaceOrientations~ipad 12 | 13 | UIInterfaceOrientationPortrait 14 | UIInterfaceOrientationLandscapeLeft 15 | UIInterfaceOrientationPortraitUpsideDown 16 | UIInterfaceOrientationLandscapeRight 17 | 18 | UISupportedInterfaceOrientations 19 | 20 | UIInterfaceOrientationPortrait 21 | 22 | CFBundleDevelopmentRegion 23 | English 24 | CFBundleDisplayName 25 | ${PRODUCT_NAME} 26 | CFBundleExecutable 27 | ${EXECUTABLE_NAME} 28 | CFBundleIconFile 29 | icon.png 30 | CFBundleIdentifier 31 | org.coenraets.WineCellar 32 | CFBundleInfoDictionaryVersion 33 | 6.0 34 | CFBundleName 35 | ${PRODUCT_NAME} 36 | CFBundlePackageType 37 | APPL 38 | CFBundleSignature 39 | ???? 40 | CFBundleVersion 41 | 1.0 42 | LSRequiresIPhoneOS 43 | 44 | NSMainNibFile 45 | 46 | NSMainNibFile~ipad 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/WineCellar-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'WineCellar' target in the 'WineCellar' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/WineCellar/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | // 20 | // main.m 21 | // WineCellar 22 | // 23 | // Created by Christophe Coenraets on 2/5/12. 24 | // Copyright Adobe 2012. All rights reserved. 25 | // 26 | 27 | #import 28 | 29 | int main(int argc, char *argv[]) { 30 | 31 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 32 | int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate"); 33 | [pool release]; 34 | return retVal; 35 | } 36 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | } 5 | 6 | .header { 7 | position: absolute; 8 | text-align: center; 9 | background: #666666; /* for non-css3 browsers */ 10 | background: -webkit-gradient(linear, left top, left bottom, from(#4B0A1E), to(#4B0A1E), color-stop(.6,#BA8A92)); 11 | color: #FFFFFF; 12 | top: 0px; 13 | left: 0px; 14 | height: 44px; 15 | right: 0px; 16 | border-bottom: solid 1px #444444; 17 | } 18 | 19 | h1 { 20 | font-size: 20px; 21 | font-weight: bold; 22 | } 23 | 24 | h2 { 25 | font-size: 18px; 26 | font-weight: bold; 27 | } 28 | 29 | .header h2 { 30 | margin-top: 0px; 31 | padding-top: 10px; 32 | } 33 | 34 | .header .nav { 35 | padding-top: 12px; 36 | position: absolute; 37 | left: 12px; 38 | } 39 | 40 | .header > .action { 41 | position: absolute; 42 | right: 10px; 43 | } 44 | 45 | .header a { 46 | text-decoration:none; 47 | color: #FFFFFF; 48 | } 49 | 50 | .content { 51 | position: absolute; 52 | top: 44px; 53 | left: 0px; 54 | bottom: 0px; 55 | right: 0px; 56 | overflow-y: scroll; 57 | } 58 | 59 | .details { 60 | padding: 12px; 61 | } 62 | 63 | .content img { 64 | float: right; 65 | width: 100px; 66 | height: 200px; 67 | } 68 | 69 | ul { 70 | list-style-type: none; 71 | padding-left: 0px; 72 | margin-top: 0px; 73 | } 74 | 75 | li a { 76 | text-decoration:none; 77 | display: block; 78 | color: #000000; 79 | border-bottom:solid 1px #CCCCCC; 80 | padding: 8px; 81 | } 82 | 83 | li a:hover { 84 | background-color: #4B0A1E; 85 | color: #BA8A92; 86 | } 87 | 88 | button { 89 | padding:6px; 90 | } -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/js/main.js: -------------------------------------------------------------------------------- 1 | Backbone.View.prototype.close = function () { 2 | console.log('Closing view ' + this); 3 | if (this.beforeClose) { 4 | this.beforeClose(); 5 | } 6 | this.remove(); 7 | this.unbind(); 8 | }; 9 | 10 | var AppRouter = Backbone.Router.extend({ 11 | 12 | routes: { 13 | "" : "list", 14 | "wines/:id" : "wineDetails" 15 | }, 16 | 17 | list: function() { 18 | this.before(function() { 19 | app.showView( new WineListView({model: app.wineList}) ); 20 | }); 21 | }, 22 | 23 | wineDetails: function(id) { 24 | this.before(function() { 25 | var wine = app.wineList.get(id); 26 | app.showView( new WineView({model: wine}) ); 27 | }); 28 | }, 29 | 30 | showView: function(view) { 31 | if (this.currentView) 32 | this.currentView.close(); 33 | $('body').html(view.render().el); 34 | this.currentView = view; 35 | return view; 36 | }, 37 | 38 | before: function(callback) { 39 | if (this.wineList) { 40 | callback(); 41 | } else { 42 | this.wineList = new WineCollection(); 43 | this.wineList.fetch({success: function() { 44 | callback(); 45 | }}); 46 | } 47 | } 48 | 49 | 50 | }); 51 | 52 | function startApp() { 53 | tpl.loadTemplates(['wine-list', 'wine-details', 'wine-list-item'], function() { 54 | app = new AppRouter(); 55 | Backbone.history.start(); 56 | }); 57 | } -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/js/models/winemodel.js: -------------------------------------------------------------------------------- 1 | window.Wine = Backbone.Model.extend({ 2 | urlRoot: "http://coenraets.org/backbone-cellar/part1/api/wines", 3 | defaults: { 4 | "id": null, 5 | "name": "", 6 | "grapes": "", 7 | "country": "USA", 8 | "region": "California", 9 | "year": "", 10 | "description": "", 11 | "picture": "" 12 | } 13 | }); 14 | 15 | window.WineCollection = Backbone.Collection.extend({ 16 | model: Wine, 17 | url: "http://coenraets.org/backbone-cellar/part1/api/wines" 18 | }); -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/js/utils.js: -------------------------------------------------------------------------------- 1 | tpl = { 2 | 3 | // Map of preloaded templates for the app 4 | templates: {}, 5 | 6 | // Recursively pre-load all the templates for the app. 7 | // This implementation should be changed in a production environment. A build script should concatenate 8 | // all the template files in a single file. 9 | loadTemplates: function(names, callback) { 10 | 11 | var that = this; 12 | 13 | var loadTemplate = function(index) { 14 | var name = names[index]; 15 | console.log('Loading template: ' + name); 16 | $.get('tpl/' + name + '.html', function(data) { 17 | that.templates[name] = data; 18 | index++; 19 | if (index < names.length) { 20 | loadTemplate(index); 21 | } else { 22 | callback(); 23 | } 24 | }, 'text'); 25 | } 26 | 27 | loadTemplate(0); 28 | }, 29 | 30 | // Get template by name from map of preloaded templates 31 | get: function(name) { 32 | return this.templates[name]; 33 | } 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/js/views/winedetails.js: -------------------------------------------------------------------------------- 1 | window.WineView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | this.template = _.template(tpl.get('wine-details')); 5 | }, 6 | 7 | render: function(eventName) { 8 | $(this.el).html(this.template(this.model.toJSON())); 9 | return this; 10 | } 11 | 12 | }); -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/js/views/winelist.js: -------------------------------------------------------------------------------- 1 | window.WineListView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | this.template = _.template(tpl.get('wine-list')); 5 | this.model.bind("reset", this.render, this); 6 | this.model.bind("add", function(wine) { 7 | $(this.el).append(new WineListItemView({model: wine}).render().el); 8 | }); 9 | }, 10 | 11 | render: function(eventName) { 12 | $(this.el).html(this.template()); 13 | var ul = $('ul', $(this.el)); 14 | _.each(this.model.models, function(wine) { 15 | ul.append(new WineListItemView({model: wine}).render().el); 16 | }, this); 17 | return this; 18 | } 19 | }); 20 | 21 | window.WineListItemView = Backbone.View.extend({ 22 | 23 | tagName: "li", 24 | 25 | initialize: function() { 26 | this.template = _.template(tpl.get('wine-list-item')); 27 | this.model.bind("change", this.render, this); 28 | this.model.bind("destroy", this.close, this); 29 | }, 30 | 31 | render: function(eventName) { 32 | $(this.el).html(this.template(this.model.toJSON())); 33 | return this; 34 | } 35 | 36 | }); -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/block_nine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/block_nine.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/bodega_lurton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/bodega_lurton.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/bouscat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/bouscat.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/domaine_serene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/domaine_serene.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/ex_umbris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/ex_umbris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/generic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/generic.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/lan_rioja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/lan_rioja.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/le_doyenne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/le_doyenne.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/lurton-pinot-gris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/lurton-pinot-gris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/margerum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/margerum.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/morizottes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/morizottes.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/rex_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/rex_hill.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/saint_cosme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/saint_cosme.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/pics/viticcio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/ios/WineCellar/www/pics/viticcio.jpg -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/tpl/wine-details.html: -------------------------------------------------------------------------------- 1 |
      2 | List 3 |

      Backbone Cellar

      4 |
      5 | 6 |
      7 |

      <%= name %>

      8 | <%= year %>
      9 | <%= grapes %>
      10 | <%= region %>, <%= country %> 11 |

      12 |

      13 | 14 | <%= description %> 15 |

      16 |
      17 | -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/tpl/wine-list-item.html: -------------------------------------------------------------------------------- 1 | <%= name %> -------------------------------------------------------------------------------- /tutorial/mobile/ios/WineCellar/www/tpl/wine-list.html: -------------------------------------------------------------------------------- 1 |
      2 |

      Backbone Cellar

      3 |
      4 | 5 |
      6 | 7 |
      8 | -------------------------------------------------------------------------------- /tutorial/mobile/offline/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | } 5 | 6 | .header { 7 | position: absolute; 8 | text-align: center; 9 | background: #666666; /* for non-css3 browsers */ 10 | background: -webkit-gradient(linear, left top, left bottom, from(#4B0A1E), to(#4B0A1E), color-stop(.6,#BA8A92)); 11 | color: #FFFFFF; 12 | top: 0px; 13 | left: 0px; 14 | height: 44px; 15 | right: 0px; 16 | border-bottom: solid 1px #444444; 17 | } 18 | 19 | h1 { 20 | font-size: 20px; 21 | font-weight: bold; 22 | } 23 | 24 | h2 { 25 | font-size: 18px; 26 | font-weight: bold; 27 | } 28 | 29 | .header h2 { 30 | margin-top: 0px; 31 | padding-top: 10px; 32 | } 33 | 34 | .header .nav { 35 | padding-top: 12px; 36 | position: absolute; 37 | left: 12px; 38 | } 39 | 40 | .header > .action { 41 | position: absolute; 42 | right: 10px; 43 | } 44 | 45 | .header a { 46 | text-decoration:none; 47 | color: #FFFFFF; 48 | } 49 | 50 | .content { 51 | position: absolute; 52 | top: 44px; 53 | left: 0px; 54 | bottom: 0px; 55 | right: 0px; 56 | overflow-y: scroll; 57 | } 58 | 59 | .details { 60 | padding: 12px; 61 | } 62 | 63 | .content img { 64 | float: right; 65 | width: 100px; 66 | height: 200px; 67 | } 68 | 69 | ul { 70 | list-style-type: none; 71 | padding-left: 0px; 72 | margin-top: 0px; 73 | } 74 | 75 | li a { 76 | text-decoration:none; 77 | display: block; 78 | color: #000000; 79 | border-bottom:solid 1px #CCCCCC; 80 | padding: 8px; 81 | } 82 | 83 | li a:hover { 84 | background-color: #4B0A1E; 85 | color: #BA8A92; 86 | } 87 | 88 | button { 89 | padding:6px; 90 | } -------------------------------------------------------------------------------- /tutorial/mobile/offline/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /tutorial/mobile/offline/js/models/winemodel.js: -------------------------------------------------------------------------------- 1 | window.Wine = Backbone.Model.extend({ 2 | urlRoot: "http://coenraets.org/backbone-cellar/part1/api/wines", 3 | dao: WineDAO 4 | }); 5 | 6 | window.WineCollection = Backbone.Collection.extend({ 7 | model: Wine, 8 | url: "http://coenraets.org/backbone-cellar/part1/api/wines", 9 | dao: WineDAO 10 | }); -------------------------------------------------------------------------------- /tutorial/mobile/offline/js/views/winedetails.js: -------------------------------------------------------------------------------- 1 | window.WineView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | var tpl = window.templateLoader; 5 | this.template = _.template(tpl.get('wine-details')); 6 | this.model.bind("change", this.render, this); 7 | }, 8 | 9 | render: function(eventName) { 10 | console.log('render'); 11 | $(this.el).html(this.template(this.model.toJSON())); 12 | return this; 13 | } 14 | 15 | }); -------------------------------------------------------------------------------- /tutorial/mobile/offline/js/views/winelist.js: -------------------------------------------------------------------------------- 1 | window.WineListView = Backbone.View.extend({ 2 | 3 | initialize: function() { 4 | var tpl = window.templateLoader; 5 | this.template = _.template(tpl.get('wine-list')); 6 | this.model.bind("reset", this.render, this); 7 | }, 8 | 9 | render: function(eventName) { 10 | console.log('list render'); 11 | $(this.el).html(this.template()); 12 | var ul = $('ul', $(this.el)); 13 | console.log(ul); 14 | _.each(this.model.models, function(wine) { 15 | ul.append(new WineListItemView({model: wine}).render().el); 16 | }, this); 17 | return this; 18 | } 19 | }); 20 | 21 | window.WineListItemView = Backbone.View.extend({ 22 | 23 | tagName: "li", 24 | 25 | initialize: function() { 26 | var tpl = window.templateLoader; 27 | this.template = _.template(tpl.get('wine-list-item')); 28 | this.model.bind("change", this.render, this); 29 | this.model.bind("destroy", this.close, this); 30 | }, 31 | 32 | render: function(eventName) { 33 | $(this.el).html(this.template(this.model.toJSON())); 34 | return this; 35 | } 36 | 37 | }); -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/block_nine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/block_nine.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/bodega_lurton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/bodega_lurton.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/bouscat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/bouscat.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/domaine_serene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/domaine_serene.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/ex_umbris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/ex_umbris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/generic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/generic.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/lan_rioja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/lan_rioja.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/le_doyenne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/le_doyenne.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/lurton-pinot-gris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/lurton-pinot-gris.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/margerum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/margerum.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/morizottes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/morizottes.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/rex_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/rex_hill.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/saint_cosme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/saint_cosme.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/pics/viticcio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/mobile/offline/pics/viticcio.jpg -------------------------------------------------------------------------------- /tutorial/mobile/offline/tpl/wine-details.html: -------------------------------------------------------------------------------- 1 |
      2 | List 3 |

      Backbone Cellar

      4 |
      5 | 6 |
      7 |

      <%= name %>

      8 | <%= year %>
      9 | <%= grapes %>
      10 | <%= region %>, <%= country %> 11 |

      12 |

      13 | 14 | <%= description %> 15 |

      16 |
      17 | -------------------------------------------------------------------------------- /tutorial/mobile/offline/tpl/wine-list-item.html: -------------------------------------------------------------------------------- 1 | <%= name %> -------------------------------------------------------------------------------- /tutorial/mobile/offline/tpl/wine-list.html: -------------------------------------------------------------------------------- 1 |
      2 |

      Backbone Cellar

      3 |
      4 | 5 |
      6 | 7 |
      8 | -------------------------------------------------------------------------------- /tutorial/part1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Backbone Cellar 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
      15 |

      Welcome to Backbone Cellar

      16 |

      17 | This is a sample application part of of three-part tutorial showing how to build a CRUD application with Backbone.js. 18 |

      19 |
      20 | 21 | 22 | 25 | 26 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /tutorial/part1/js/main.js: -------------------------------------------------------------------------------- 1 | // Models 2 | window.Wine = Backbone.Model.extend(); 3 | 4 | window.WineCollection = Backbone.Collection.extend({ 5 | model:Wine, 6 | url:"../api/wines" 7 | }); 8 | 9 | 10 | // Views 11 | window.WineListView = Backbone.View.extend({ 12 | 13 | tagName:'ul', 14 | 15 | initialize:function () { 16 | this.model.bind("reset", this.render, this); 17 | }, 18 | 19 | render:function (eventName) { 20 | _.each(this.model.models, function (wine) { 21 | $(this.el).append(new WineListItemView({model:wine}).render().el); 22 | }, this); 23 | return this; 24 | } 25 | 26 | }); 27 | 28 | window.WineListItemView = Backbone.View.extend({ 29 | 30 | tagName:"li", 31 | 32 | template:_.template($('#tpl-wine-list-item').html()), 33 | 34 | render:function (eventName) { 35 | $(this.el).html(this.template(this.model.toJSON())); 36 | return this; 37 | } 38 | 39 | }); 40 | 41 | window.WineView = Backbone.View.extend({ 42 | 43 | template:_.template($('#tpl-wine-details').html()), 44 | 45 | render:function (eventName) { 46 | $(this.el).html(this.template(this.model.toJSON())); 47 | return this; 48 | } 49 | 50 | }); 51 | 52 | 53 | // Router 54 | var AppRouter = Backbone.Router.extend({ 55 | 56 | routes:{ 57 | "":"list", 58 | "wines/:id":"wineDetails" 59 | }, 60 | 61 | list:function () { 62 | this.wineList = new WineCollection(); 63 | this.wineListView = new WineListView({model:this.wineList}); 64 | this.wineList.fetch(); 65 | $('#sidebar').html(this.wineListView.render().el); 66 | }, 67 | 68 | wineDetails:function (id) { 69 | this.wine = this.wineList.get(id); 70 | this.wineView = new WineView({model:this.wine}); 71 | $('#content').html(this.wineView.render().el); 72 | } 73 | }); 74 | 75 | var app = new AppRouter(); 76 | Backbone.history.start(); -------------------------------------------------------------------------------- /tutorial/part2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Backbone Cellar 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
      15 |

      Welcome to Backbone Cellar

      16 |

      17 | This is a sample application part of of three-part tutorial showing how to build a CRUD application with Backbone.js. 18 |

      19 |
      20 | 21 | 22 | 26 | 27 | 30 | 31 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /tutorial/part2/js/main.js: -------------------------------------------------------------------------------- 1 | // Models 2 | window.Wine = Backbone.Model.extend({ 3 | urlRoot:"../api/wines", 4 | defaults:{ 5 | "id":null, 6 | "name":"", 7 | "grapes":"", 8 | "country":"USA", 9 | "region":"California", 10 | "year":"", 11 | "description":"", 12 | "picture":"" 13 | } 14 | }); 15 | 16 | window.WineCollection = Backbone.Collection.extend({ 17 | model:Wine, 18 | url:"../api/wines" 19 | }); 20 | 21 | 22 | // Views 23 | window.WineListView = Backbone.View.extend({ 24 | 25 | tagName:'ul', 26 | 27 | initialize:function () { 28 | this.model.bind("reset", this.render, this); 29 | var self = this; 30 | this.model.bind("add", function (wine) { 31 | $(self.el).append(new WineListItemView({model:wine}).render().el); 32 | }); 33 | }, 34 | 35 | render:function (eventName) { 36 | _.each(this.model.models, function (wine) { 37 | $(this.el).append(new WineListItemView({model:wine}).render().el); 38 | }, this); 39 | return this; 40 | } 41 | }); 42 | 43 | window.WineListItemView = Backbone.View.extend({ 44 | 45 | tagName:"li", 46 | 47 | template:_.template($('#tpl-wine-list-item').html()), 48 | 49 | initialize:function () { 50 | this.model.bind("change", this.render, this); 51 | this.model.bind("destroy", this.close, this); 52 | }, 53 | 54 | render:function (eventName) { 55 | $(this.el).html(this.template(this.model.toJSON())); 56 | return this; 57 | }, 58 | 59 | close:function () { 60 | $(this.el).unbind(); 61 | $(this.el).remove(); 62 | } 63 | }); 64 | 65 | window.WineView = Backbone.View.extend({ 66 | 67 | template:_.template($('#tpl-wine-details').html()), 68 | 69 | initialize:function () { 70 | this.model.bind("change", this.render, this); 71 | }, 72 | 73 | render:function (eventName) { 74 | $(this.el).html(this.template(this.model.toJSON())); 75 | return this; 76 | }, 77 | 78 | events:{ 79 | "change input":"change", 80 | "click .save":"saveWine", 81 | "click .delete":"deleteWine" 82 | }, 83 | 84 | change:function (event) { 85 | var target = event.target; 86 | console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value); 87 | // You could change your model on the spot, like this: 88 | // var change = {}; 89 | // change[target.name] = target.value; 90 | // this.model.set(change); 91 | }, 92 | 93 | saveWine:function () { 94 | this.model.set({ 95 | name:$('#name').val(), 96 | grapes:$('#grapes').val(), 97 | country:$('#country').val(), 98 | region:$('#region').val(), 99 | year:$('#year').val(), 100 | description:$('#description').val() 101 | }); 102 | if (this.model.isNew()) { 103 | app.wineList.create(this.model); 104 | } else { 105 | this.model.save(); 106 | } 107 | return false; 108 | }, 109 | 110 | deleteWine:function () { 111 | this.model.destroy({ 112 | success:function () { 113 | alert('Wine deleted successfully'); 114 | window.history.back(); 115 | } 116 | }); 117 | return false; 118 | }, 119 | 120 | close:function () { 121 | $(this.el).unbind(); 122 | $(this.el).empty(); 123 | } 124 | }); 125 | 126 | window.HeaderView = Backbone.View.extend({ 127 | 128 | template:_.template($('#tpl-header').html()), 129 | 130 | initialize:function () { 131 | this.render(); 132 | }, 133 | 134 | render:function (eventName) { 135 | $(this.el).html(this.template()); 136 | return this; 137 | }, 138 | 139 | events:{ 140 | "click .new":"newWine" 141 | }, 142 | 143 | newWine:function (event) { 144 | if (app.wineView) app.wineView.close(); 145 | app.wineView = new WineView({model:new Wine()}); 146 | $('#content').html(app.wineView.render().el); 147 | return false; 148 | } 149 | }); 150 | 151 | 152 | // Router 153 | var AppRouter = Backbone.Router.extend({ 154 | 155 | routes:{ 156 | "":"list", 157 | "wines/:id":"wineDetails" 158 | }, 159 | 160 | initialize:function () { 161 | $('#header').html(new HeaderView().render().el); 162 | }, 163 | 164 | list:function () { 165 | this.wineList = new WineCollection(); 166 | this.wineListView = new WineListView({model:this.wineList}); 167 | this.wineList.fetch(); 168 | $('#sidebar').html(this.wineListView.render().el); 169 | }, 170 | 171 | wineDetails:function (id) { 172 | this.wine = this.wineList.get(id); 173 | if (app.wineView) app.wineView.close(); 174 | this.wineView = new WineView({model:this.wine}); 175 | $('#content').html(this.wineView.render().el); 176 | } 177 | 178 | }); 179 | 180 | var app = new AppRouter(); 181 | Backbone.history.start(); -------------------------------------------------------------------------------- /tutorial/part3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Backbone Cellar 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 |
      17 |

      Welcome to Backbone Cellar

      18 |

      19 | This is a sample application part of of three-part tutorial showing how to build a CRUD application with Backbone.js. 20 |

      21 |
      22 | 23 | 24 | 27 | 28 | 31 | 32 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /tutorial/pics/argiano.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/argiano.jpg -------------------------------------------------------------------------------- /tutorial/pics/block_nine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/block_nine.jpg -------------------------------------------------------------------------------- /tutorial/pics/bodega_lurton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/bodega_lurton.jpg -------------------------------------------------------------------------------- /tutorial/pics/bouscat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/bouscat.jpg -------------------------------------------------------------------------------- /tutorial/pics/calera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/calera.jpg -------------------------------------------------------------------------------- /tutorial/pics/capineto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/capineto.png -------------------------------------------------------------------------------- /tutorial/pics/caronne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/caronne.jpg -------------------------------------------------------------------------------- /tutorial/pics/dinastia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/dinastia.jpg -------------------------------------------------------------------------------- /tutorial/pics/domaine_serene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/domaine_serene.jpg -------------------------------------------------------------------------------- /tutorial/pics/ex_umbris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/ex_umbris.jpg -------------------------------------------------------------------------------- /tutorial/pics/fourvines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/fourvines.jpg -------------------------------------------------------------------------------- /tutorial/pics/generic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/generic.jpg -------------------------------------------------------------------------------- /tutorial/pics/hugel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/hugel.jpg -------------------------------------------------------------------------------- /tutorial/pics/lan_rioja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/lan_rioja.jpg -------------------------------------------------------------------------------- /tutorial/pics/le_doyenne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/le_doyenne.jpg -------------------------------------------------------------------------------- /tutorial/pics/lurton-pinot-gris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/lurton-pinot-gris.jpg -------------------------------------------------------------------------------- /tutorial/pics/margerum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/margerum.jpg -------------------------------------------------------------------------------- /tutorial/pics/momo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/momo.jpg -------------------------------------------------------------------------------- /tutorial/pics/morizottes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/morizottes.jpg -------------------------------------------------------------------------------- /tutorial/pics/petalos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/petalos.jpg -------------------------------------------------------------------------------- /tutorial/pics/ponzi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/ponzi.jpg -------------------------------------------------------------------------------- /tutorial/pics/quivira.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/quivira.jpg -------------------------------------------------------------------------------- /tutorial/pics/rex_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/rex_hill.jpg -------------------------------------------------------------------------------- /tutorial/pics/saint_cosme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/saint_cosme.jpg -------------------------------------------------------------------------------- /tutorial/pics/shafer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/shafer.jpg -------------------------------------------------------------------------------- /tutorial/pics/viticcio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/viticcio.jpg -------------------------------------------------------------------------------- /tutorial/pics/waterbrook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/backbone-cellar/f46548111e420d5e02e99fecbedebf6cce825799/tutorial/pics/waterbrook.jpg -------------------------------------------------------------------------------- /tutorial/readme.md: -------------------------------------------------------------------------------- 1 | # Backbone.js Wine Cellar Tutorial # 2 | 3 | "Backbone Cellar" is a sample [Backbone.js](http://documentcloud.github.com/backbone/) application. 4 | The application allows you to browse through a list of wines, as well as add, update, and delete wines. 5 | 6 | "Backbone Cellar" is the application used in the four-part tutorial posted here: [part 1](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-1-getting-started/), [part 2](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-2-crud/), [part 3](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-3-deep-linking-and-application-states/), [part 4](http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/). 7 | 8 | If you just want to look at the final version of the application, look in the [final](https://github.com/ccoenraets/backbone-cellar/tree/master/final) directory. 9 | 10 | You can also follow the tutorial and build the application step by step: 11 | 12 | - In Part 1 ([doc](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-1-getting-started/) - [code](https://github.com/ccoenraets/backbone-cellar/tree/master/part1)), you define the basic infrastructure. You create a “read-only” version of the application: you’ll be able to retrieve a list of wine and get the details of each wine. 13 | - In Part 2 ([doc](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-2-crud/) - [code](https://github.com/ccoenraets/backbone-cellar/tree/master/part2)), you add the code to add, update and delete wines. You leverage Backbone’s powerful REST integration. 14 | - In Part 3 ([doc](http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-3-deep-linking-and-application-states/) - [code](https://github.com/ccoenraets/backbone-cellar/tree/master/part3)), you add complete support for history management and deep linking. 15 | - In Part 4 ([doc](http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/) - [code](https://github.com/ccoenraets/backbone-cellar/tree/master/final)), you load templates asynchronously and implement a few best practices. 16 | 17 | The UI is intentionally plain to keep the focus on the architecture of the application. 18 | 19 | ## Set Up: ## 20 | 21 | 1. Create a MySQL database name "cellar". 22 | 2. Execute cellar.sql to create and populate the "wine" table: 23 | 24 | mysql cellar -uroot < cellar.sql 25 | 26 | ## Services: ## 27 | 28 | The application is available with a PHP or Java services: 29 | 30 | - The PHP services are available in the api directory of this repository. The RESTful services are implemented in PHP using the [Slim framework](http://www.slimframework.com/) (also included in the api directory). 31 | - The Java back-end is available in [this repository](https://github.com/ccoenraets/backbone-jax-cellar). The RESTful services are implemented in Java using JAX-RS. 32 | 33 | Your feedback is appreciated. Please post your questions and comments in the blog posts referenced above. --------------------------------------------------------------------------------