├── .gitignore
├── README.md
├── api
├── app.js
├── app.json
├── datasources.json
├── models.json
├── models
│ └── .gitkeep
└── package.json
└── client
├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── app
├── .buildignore
├── .htaccess
├── 404.html
├── favicon.ico
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── images
│ └── yeoman.png
├── index.html
├── robots.txt
├── scripts
│ ├── app.js
│ ├── controllers
│ │ ├── main.js
│ │ └── notes.js
│ └── factory.js
├── styles
│ └── main.css
└── views
│ ├── main.html
│ └── notes
│ ├── edit.html
│ ├── item.html
│ ├── list.html
│ └── main.html
├── bower.json
├── package.json
└── test
├── .jshintrc
├── runner.html
└── spec
└── controllers
├── items.js
└── main.js
/.gitignore:
--------------------------------------------------------------------------------
1 | api/node_modules
2 | client/node_modules
3 | client/dist
4 | client/.tmp
5 | client/.sass-cache
6 | client/app/bower_components
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### If you like this my [new project](https://github.com/beeman/loopback-angular-admin) might be interesting too!
2 |
3 | It uses loopback, angular, restangular and has support for user authentication, file uploads and other stuff :)
4 |
5 | # angular-restangular-crud
6 |
7 | Example of how to use restangular in a way that you DRY too much...
8 |
9 | It uses AngularJS with Restangular at the frontend and loopback (https://www.npmjs.org/package/loopback) as the API.
10 |
11 | To run it you need node/npm/grunt/bower (and maybe even more packages) installed...
12 |
13 | ### Clone the repository
14 |
15 | git clone https://github.com/beeman/angular-restangular-crud
16 |
17 | ### Install the server and leave it running
18 |
19 | cd angular-restangular-crud/api
20 | npm install
21 | node app.js # or nodemon app.js
22 |
23 | ### Install the client
24 |
25 | cd angular-restangular-crud/client
26 | npm install
27 | bower install
28 | grunt
29 |
30 | Grunt might show some errors, most of the time it's jslint testing your JavaScript... Fix the errors or override them with --force.
31 |
32 | ### Serve the page
33 |
34 | grunt serve
35 |
36 | If all went well you can now list/add/view and edit notes.
37 |
38 | You can start working in the client directory and most of the changes will be picked up by autoreload (https://www.npmjs.org/package/livereload). Grunt serves the client on port 9000 and the API serves the same app at port 3000, but without the livereload.
39 |
40 | It is trivial to create some more models and quickly crank out a simple scaffold, as show below.
41 |
42 | ## Adding a new datatype to this example
43 |
44 | We will add an object named 'Host'
45 |
46 | Add menu item somewhere around line 30 in app/index.html:
47 |
48 |
Hosts
49 |
50 | Copy app/scripts/controllers/notes.js to app/scripts/controllers/hosts.js
51 |
52 | $ cp app/scripts/controllers/notes.js app/scripts/controllers/hosts.js
53 |
54 | Now edit the new file and search and replace:
55 |
56 | find app/scripts/controllers/hosts.js -type f -exec sed -i '' 's/note/host/g' {} \;
57 | find app/scripts/controllers/hosts.js -type f -exec sed -i '' 's/Note/Host/g' {} \;
58 |
59 | Create the views:
60 |
61 | mkdir app/views/hosts/
62 | cp app/views/notes/* app/views/hosts
63 |
64 | Now edit these new files and search and replace:
65 |
66 | find app/views/hosts/. -type f -exec sed -i '' 's/note/host/g' {} \;
67 | find app/views/hosts/. -type f -exec sed -i '' 's/Note/Host/g' {} \;
68 |
69 | Last but certainly not least, register the new controller in your template:
70 |
71 | Edit app/index.html and the following line just before the 'endbuild' line
72 |
73 |
74 |
75 |
76 | Update the API to accept this datatype. Edit api/models.json and add the following code to the object array:
77 |
78 | "host": {
79 | "public": true,
80 | "dataSource": "db",
81 | "plural": "hosts"
82 | }
83 |
84 | Now restart the API to start accepting the model abov..
85 |
86 | # Todo
87 |
88 | * Add easy DB configuration
89 | * You name it?
90 |
91 | # License
92 |
93 | The MIT License (MIT)
94 |
95 | Copyright (c) 2014 Bram Borggreve
96 |
97 | Permission is hereby granted, free of charge, to any person obtaining a copy
98 | of this software and associated documentation files (the "Software"), to deal
99 | in the Software without restriction, including without limitation the rights
100 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
101 | copies of the Software, and to permit persons to whom the Software is
102 | furnished to do so, subject to the following conditions:
103 |
104 | The above copyright notice and this permission notice shall be included in
105 | all copies or substantial portions of the Software.
106 |
107 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
108 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
109 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
110 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
111 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
112 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
113 | THE SOFTWARE.
114 |
--------------------------------------------------------------------------------
/api/app.js:
--------------------------------------------------------------------------------
1 | 'use struct';
2 |
3 | var loopback = require('loopback');
4 | var path = require('path');
5 | var app = module.exports = loopback();
6 | var started = new Date();
7 |
8 | /*
9 | * 1. Configure LoopBack models and datasources
10 | *
11 | * Read more at http://apidocs.strongloop.com/loopback#appbootoptions
12 | */
13 |
14 | app.boot(__dirname);
15 |
16 | /*
17 | * 2. Configure request preprocessing
18 | *
19 | * LoopBack support all express-compatible middleware.
20 | */
21 |
22 | app.use(loopback.favicon());
23 | app.use(loopback.logger(app.get('env') === 'development' ? 'dev' : 'default'));
24 | app.use(loopback.cookieParser(app.get('cookieSecret')));
25 | app.use(loopback.token({model: app.models.accessToken}));
26 | app.use(loopback.bodyParser());
27 | app.use(loopback.methodOverride());
28 |
29 | /*
30 | * EXTENSION POINT
31 | * Add your custom request-preprocessing middleware here.
32 | * Example:
33 | * app.use(loopback.limit('5.5mb'))
34 | */
35 |
36 | /*
37 | * 3. Setup request handlers.
38 | */
39 |
40 | // LoopBack REST interface
41 | app.use(app.get('restApiRoot'), loopback.rest());
42 |
43 | // API explorer (if present)
44 | try {
45 | var explorer = require('loopback-explorer')(app);
46 | app.use('/explorer', explorer);
47 | app.once('started', function(baseUrl) {
48 | console.log('Browse your REST API at %s%s', baseUrl, explorer.route);
49 | });
50 | } catch(e){
51 | console.log(
52 | 'Run `npm install loopback-explorer` to enable the LoopBack explorer'
53 | );
54 | }
55 |
56 | /*
57 | * EXTENSION POINT
58 | * Add your custom request-handling middleware here.
59 | * Example:
60 | * app.use(function(req, resp, next) {
61 | * if (req.url == '/status') {
62 | * // send status response
63 | * } else {
64 | * next();
65 | * }
66 | * });
67 | */
68 |
69 | // Let express routes handle requests that were not handled
70 | // by any of the middleware registered above.
71 | // This way LoopBack REST and API Explorer take precedence over
72 | // express routes.
73 | app.use(app.router);
74 |
75 | // The static file server should come after all other routes
76 | // Every request that goes through the static middleware hits
77 | // the file system to check if a file exists.
78 | app.use(loopback.static(path.join(__dirname, '../client/app/')));
79 |
80 | // Requests that get this far won't be handled
81 | // by any middleware. Convert them into a 404 error
82 | // that will be handled later down the chain.
83 | app.use(loopback.urlNotFound());
84 |
85 | /*
86 | * 4. Setup error handling strategy
87 | */
88 |
89 | /*
90 | * EXTENSION POINT
91 | * Add your custom error reporting middleware here
92 | * Example:
93 | * app.use(function(err, req, resp, next) {
94 | * console.log(req.url, ' failed: ', err.stack);
95 | * next(err);
96 | * });
97 | */
98 |
99 | // The ultimate error handler.
100 | app.use(loopback.errorHandler());
101 |
102 |
103 | /*
104 | * 5. Add a basic application status route at the root `/`.
105 | *
106 | * (remove this to handle `/` on your own)
107 | */
108 |
109 | app.get('/status', loopback.status());
110 |
111 | /*
112 | * 6. Enable access control and token based authentication.
113 | */
114 |
115 | var swaggerRemote = app.remotes().exports.swagger;
116 | if (swaggerRemote) swaggerRemote.requireToken = false;
117 |
118 | app.enableAuth();
119 |
120 | /*
121 | * 7. Optionally start the server
122 | *
123 | * (only if this module is the main module)
124 | */
125 |
126 | app.start = function() {
127 | return app.listen(function() {
128 | var baseUrl = 'http://' + app.get('host') + ':' + app.get('port');
129 | app.emit('started', baseUrl);
130 | console.log('LoopBack server listening @ %s%s', baseUrl, '/');
131 | });
132 | };
133 |
134 | if(require.main === module) {
135 | app.start();
136 | }
137 |
--------------------------------------------------------------------------------
/api/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 3000,
3 | "host": "0.0.0.0",
4 | "cookieSecret": "499a743d-0ea1-41c0-a8a1-cde6630dd347"
5 | }
6 |
--------------------------------------------------------------------------------
/api/datasources.json:
--------------------------------------------------------------------------------
1 | {
2 | "db": {
3 | "defaultForType": "db",
4 | "connector": "memory"
5 | },
6 | "push": {
7 | "defaultForType": "push",
8 | "connector": "loopback-push-notification",
9 | "installation": "installation",
10 | "notification": "notification",
11 | "application": "application"
12 | },
13 | "mail": {
14 | "defaultForType": "mail",
15 | "connector": "mail"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/api/models.json:
--------------------------------------------------------------------------------
1 | {
2 | "email": {
3 | "options": {
4 | "base": "Email"
5 | },
6 | "dataSource": "mail",
7 | "public": false
8 | },
9 | "user": {
10 | "options": {
11 | "base": "User",
12 | "relations": {
13 | "accessTokens": {
14 | "model": "accessToken",
15 | "type": "hasMany",
16 | "foreignKey": "userId"
17 | }
18 | }
19 | },
20 | "dataSource": "db",
21 | "public": true
22 | },
23 | "accessToken": {
24 | "options": {
25 | "base": "AccessToken"
26 | },
27 | "dataSource": "db",
28 | "public": true
29 | },
30 | "application": {
31 | "options": {
32 | "base": "Application"
33 | },
34 | "dataSource": "db",
35 | "public": true
36 | },
37 | "acl": {
38 | "options": {
39 | "base": "ACL"
40 | },
41 | "dataSource": "db",
42 | "public": false
43 | },
44 | "roleMapping": {
45 | "options": {
46 | "base": "RoleMapping"
47 | },
48 | "dataSource": "db",
49 | "public": false
50 | },
51 | "role": {
52 | "options": {
53 | "base": "Role",
54 | "relations": {
55 | "principals": {
56 | "type": "hasMany",
57 | "model": "roleMapping",
58 | "foreignKey": "roleId"
59 | }
60 | }
61 | },
62 | "dataSource": "db",
63 | "public": false
64 | },
65 | "scope": {
66 | "options": {
67 | "base": "Scope"
68 | },
69 | "dataSource": "db",
70 | "public": false
71 | },
72 | "push": {
73 | "options": {
74 | "base": "Push",
75 | "plural": "push"
76 | },
77 | "dataSource": "push"
78 | },
79 | "installation": {
80 | "options": {
81 | "base": "Installation"
82 | },
83 | "dataSource": "db",
84 | "public": true
85 | },
86 | "notification": {
87 | "options": {
88 | "base": "Notification"
89 | },
90 | "dataSource": "db",
91 | "public": true
92 | },
93 | "note": {
94 | "public": true,
95 | "dataSource": "db",
96 | "plural": "notes"
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/api/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/api/models/.gitkeep
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "main": "app.js",
4 | "bin": "server.js",
5 | "scripts": {
6 | "start": "node app.js"
7 | },
8 | "dependencies": {
9 | "loopback": "1.*",
10 | "loopback-datasource-juggler" : "1.*"
11 | },
12 | "optionalDependencies": {
13 | "loopback-explorer": "~1.1.0",
14 | "loopback-push-notification": "~1.1.0"
15 | },
16 | "name": "angular-scaffold-api"
17 | }
18 |
--------------------------------------------------------------------------------
/client/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/client/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/client/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .tmp
4 | .sass-cache
5 | app/bower_components
6 |
--------------------------------------------------------------------------------
/client/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "angular": false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2014-02-12 using generator-angular 0.7.1
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 |
12 | // Load grunt tasks automatically
13 | require('load-grunt-tasks')(grunt);
14 |
15 | // Time how long tasks take. Can help when optimizing build times
16 | require('time-grunt')(grunt);
17 |
18 | // Define the configuration for all the tasks
19 | grunt.initConfig({
20 |
21 | // Project settings
22 | yeoman: {
23 | // configurable paths
24 | app: require('./bower.json').appPath || 'app',
25 | dist: 'dist'
26 | },
27 |
28 | // Watches files for changes and runs tasks based on the changed files
29 | watch: {
30 | js: {
31 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
32 | tasks: ['newer:jshint:all'],
33 | options: {
34 | livereload: true
35 | }
36 | },
37 | jsTest: {
38 | files: ['test/spec/{,*/}*.js'],
39 | tasks: ['newer:jshint:test', 'karma']
40 | },
41 | styles: {
42 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
43 | tasks: ['newer:copy:styles', 'autoprefixer']
44 | },
45 | gruntfile: {
46 | files: ['Gruntfile.js']
47 | },
48 | livereload: {
49 | options: {
50 | livereload: '<%= connect.options.livereload %>'
51 | },
52 | files: [
53 | '<%= yeoman.app %>/{,*/}*.html',
54 | '.tmp/styles/{,*/}*.css',
55 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
56 | ]
57 | }
58 | },
59 |
60 | // The actual grunt server settings
61 | connect: {
62 | options: {
63 | port: 9000,
64 | // Change this to '0.0.0.0' to access the server from outside.
65 | hostname: 'localhost',
66 | livereload: 35729
67 | },
68 | livereload: {
69 | options: {
70 | open: true,
71 | base: [
72 | '.tmp',
73 | '<%= yeoman.app %>'
74 | ]
75 | }
76 | },
77 | test: {
78 | options: {
79 | port: 9001,
80 | base: [
81 | '.tmp',
82 | 'test',
83 | '<%= yeoman.app %>'
84 | ]
85 | }
86 | },
87 | dist: {
88 | options: {
89 | base: '<%= yeoman.dist %>'
90 | }
91 | }
92 | },
93 |
94 | // Make sure code styles are up to par and there are no obvious mistakes
95 | jshint: {
96 | options: {
97 | jshintrc: '.jshintrc',
98 | reporter: require('jshint-stylish')
99 | },
100 | all: [
101 | 'Gruntfile.js',
102 | '<%= yeoman.app %>/scripts/{,*/}*.js'
103 | ],
104 | test: {
105 | options: {
106 | jshintrc: 'test/.jshintrc'
107 | },
108 | src: ['test/spec/{,*/}*.js']
109 | }
110 | },
111 |
112 | // Empties folders to start fresh
113 | clean: {
114 | dist: {
115 | files: [{
116 | dot: true,
117 | src: [
118 | '.tmp',
119 | '<%= yeoman.dist %>/*',
120 | '!<%= yeoman.dist %>/.git*'
121 | ]
122 | }]
123 | },
124 | server: '.tmp'
125 | },
126 |
127 | // Add vendor prefixed styles
128 | autoprefixer: {
129 | options: {
130 | browsers: ['last 1 version']
131 | },
132 | dist: {
133 | files: [{
134 | expand: true,
135 | cwd: '.tmp/styles/',
136 | src: '{,*/}*.css',
137 | dest: '.tmp/styles/'
138 | }]
139 | }
140 | },
141 |
142 | // Automatically inject Bower components into the app
143 | 'bower-install': {
144 | app: {
145 | html: '<%= yeoman.app %>/index.html',
146 | ignorePath: '<%= yeoman.app %>/'
147 | }
148 | },
149 |
150 |
151 |
152 |
153 |
154 | // Renames files for browser caching purposes
155 | rev: {
156 | dist: {
157 | files: {
158 | src: [
159 | '<%= yeoman.dist %>/scripts/{,*/}*.js',
160 | '<%= yeoman.dist %>/styles/{,*/}*.css',
161 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
162 | '<%= yeoman.dist %>/styles/fonts/*'
163 | ]
164 | }
165 | }
166 | },
167 |
168 | // Reads HTML for usemin blocks to enable smart builds that automatically
169 | // concat, minify and revision files. Creates configurations in memory so
170 | // additional tasks can operate on them
171 | useminPrepare: {
172 | html: '<%= yeoman.app %>/index.html',
173 | options: {
174 | dest: '<%= yeoman.dist %>'
175 | }
176 | },
177 |
178 | // Performs rewrites based on rev and the useminPrepare configuration
179 | usemin: {
180 | html: ['<%= yeoman.dist %>/{,*/}*.html'],
181 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
182 | options: {
183 | assetsDirs: ['<%= yeoman.dist %>']
184 | }
185 | },
186 |
187 | // The following *-min tasks produce minified files in the dist folder
188 | imagemin: {
189 | dist: {
190 | files: [{
191 | expand: true,
192 | cwd: '<%= yeoman.app %>/images',
193 | src: '{,*/}*.{png,jpg,jpeg,gif}',
194 | dest: '<%= yeoman.dist %>/images'
195 | }]
196 | }
197 | },
198 | svgmin: {
199 | dist: {
200 | files: [{
201 | expand: true,
202 | cwd: '<%= yeoman.app %>/images',
203 | src: '{,*/}*.svg',
204 | dest: '<%= yeoman.dist %>/images'
205 | }]
206 | }
207 | },
208 | htmlmin: {
209 | dist: {
210 | options: {
211 | collapseWhitespace: true,
212 | collapseBooleanAttributes: true,
213 | removeCommentsFromCDATA: true,
214 | removeOptionalTags: true
215 | },
216 | files: [{
217 | expand: true,
218 | cwd: '<%= yeoman.dist %>',
219 | src: ['*.html', 'views/{,*/}*.html'],
220 | dest: '<%= yeoman.dist %>'
221 | }]
222 | }
223 | },
224 |
225 | // Allow the use of non-minsafe AngularJS files. Automatically makes it
226 | // minsafe compatible so Uglify does not destroy the ng references
227 | ngmin: {
228 | dist: {
229 | files: [{
230 | expand: true,
231 | cwd: '.tmp/concat/scripts',
232 | src: '*.js',
233 | dest: '.tmp/concat/scripts'
234 | }]
235 | }
236 | },
237 |
238 | // Replace Google CDN references
239 | cdnify: {
240 | dist: {
241 | html: ['<%= yeoman.dist %>/*.html']
242 | }
243 | },
244 |
245 | // Copies remaining files to places other tasks can use
246 | copy: {
247 | dist: {
248 | files: [{
249 | expand: true,
250 | dot: true,
251 | cwd: '<%= yeoman.app %>',
252 | dest: '<%= yeoman.dist %>',
253 | src: [
254 | '*.{ico,png,txt}',
255 | '.htaccess',
256 | '*.html',
257 | 'views/{,*/}*.html',
258 | 'bower_components/**/*',
259 | 'images/{,*/}*.{webp}',
260 | 'fonts/*'
261 | ]
262 | }, {
263 | expand: true,
264 | cwd: '.tmp/images',
265 | dest: '<%= yeoman.dist %>/images',
266 | src: ['generated/*']
267 | }]
268 | },
269 | styles: {
270 | expand: true,
271 | cwd: '<%= yeoman.app %>/styles',
272 | dest: '.tmp/styles/',
273 | src: '{,*/}*.css'
274 | }
275 | },
276 |
277 | // Run some tasks in parallel to speed up the build process
278 | concurrent: {
279 | server: [
280 | 'copy:styles'
281 | ],
282 | test: [
283 | 'copy:styles'
284 | ],
285 | dist: [
286 | 'copy:styles',
287 | 'imagemin',
288 | 'svgmin'
289 | ]
290 | },
291 |
292 | // By default, your `index.html`'s will take care of
293 | // minification. These next options are pre-configured if you do not wish
294 | // to use the Usemin blocks.
295 | // cssmin: {
296 | // dist: {
297 | // files: {
298 | // '<%= yeoman.dist %>/styles/main.css': [
299 | // '.tmp/styles/{,*/}*.css',
300 | // '<%= yeoman.app %>/styles/{,*/}*.css'
301 | // ]
302 | // }
303 | // }
304 | // },
305 | // uglify: {
306 | // dist: {
307 | // files: {
308 | // '<%= yeoman.dist %>/scripts/scripts.js': [
309 | // '<%= yeoman.dist %>/scripts/scripts.js'
310 | // ]
311 | // }
312 | // }
313 | // },
314 | // concat: {
315 | // dist: {}
316 | // },
317 |
318 | // Test settings
319 | karma: {
320 | unit: {
321 | configFile: 'karma.conf.js',
322 | singleRun: true
323 | }
324 | }
325 | });
326 |
327 |
328 | grunt.registerTask('serve', function (target) {
329 | if (target === 'dist') {
330 | return grunt.task.run(['build', 'connect:dist:keepalive']);
331 | }
332 |
333 | grunt.task.run([
334 | 'clean:server',
335 | 'bower-install',
336 | 'concurrent:server',
337 | 'autoprefixer',
338 | 'connect:livereload',
339 | 'watch'
340 | ]);
341 | });
342 |
343 | grunt.registerTask('server', function () {
344 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
345 | grunt.task.run(['serve']);
346 | });
347 |
348 | grunt.registerTask('test', [
349 | 'clean:server',
350 | 'concurrent:test',
351 | 'autoprefixer',
352 | 'connect:test',
353 | ]);
354 |
355 | grunt.registerTask('build', [
356 | 'clean:dist',
357 | 'bower-install',
358 | 'useminPrepare',
359 | 'concurrent:dist',
360 | 'autoprefixer',
361 | 'concat',
362 | 'ngmin',
363 | 'copy:dist',
364 | 'cdnify',
365 | 'cssmin',
366 | 'uglify',
367 | 'rev',
368 | 'usemin',
369 | 'htmlmin'
370 | ]);
371 |
372 | grunt.registerTask('default', [
373 | 'newer:jshint',
374 | 'test',
375 | 'build'
376 | ]);
377 | };
378 |
--------------------------------------------------------------------------------
/client/app/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/client/app/.htaccess:
--------------------------------------------------------------------------------
1 | # Apache Configuration File
2 |
3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access
4 | # to the main server config file (usually called `httpd.conf`), you should add
5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
6 |
7 | # ##############################################################################
8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) #
9 | # ##############################################################################
10 |
11 | # ------------------------------------------------------------------------------
12 | # | Cross-domain AJAX requests |
13 | # ------------------------------------------------------------------------------
14 |
15 | # Enable cross-origin AJAX requests.
16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
17 | # http://enable-cors.org/
18 |
19 | #
20 | # Header set Access-Control-Allow-Origin "*"
21 | #
22 |
23 | # ------------------------------------------------------------------------------
24 | # | CORS-enabled images |
25 | # ------------------------------------------------------------------------------
26 |
27 | # Send the CORS header for images when browsers request it.
28 | # https://developer.mozilla.org/en/CORS_Enabled_Image
29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
31 |
32 |
33 |
34 |
35 | SetEnvIf Origin ":" IS_CORS
36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS
37 |
38 |
39 |
40 |
41 | # ------------------------------------------------------------------------------
42 | # | Web fonts access |
43 | # ------------------------------------------------------------------------------
44 |
45 | # Allow access from all domains for web fonts
46 |
47 |
48 |
49 | Header set Access-Control-Allow-Origin "*"
50 |
51 |
52 |
53 |
54 | # ##############################################################################
55 | # # ERRORS #
56 | # ##############################################################################
57 |
58 | # ------------------------------------------------------------------------------
59 | # | 404 error prevention for non-existing redirected folders |
60 | # ------------------------------------------------------------------------------
61 |
62 | # Prevent Apache from returning a 404 error for a rewrite if a directory
63 | # with the same name does not exist.
64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
65 | # http://www.webmasterworld.com/apache/3808792.htm
66 |
67 | Options -MultiViews
68 |
69 | # ------------------------------------------------------------------------------
70 | # | Custom error messages / pages |
71 | # ------------------------------------------------------------------------------
72 |
73 | # You can customize what Apache returns to the client in case of an error (see
74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
75 |
76 | ErrorDocument 404 /404.html
77 |
78 |
79 | # ##############################################################################
80 | # # INTERNET EXPLORER #
81 | # ##############################################################################
82 |
83 | # ------------------------------------------------------------------------------
84 | # | Better website experience |
85 | # ------------------------------------------------------------------------------
86 |
87 | # Force IE to render pages in the highest available mode in the various
88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
89 |
90 |
91 | Header set X-UA-Compatible "IE=edge"
92 | # `mod_headers` can't match based on the content-type, however, we only
93 | # want to send this header for HTML pages and not for the other resources
94 |
95 | Header unset X-UA-Compatible
96 |
97 |
98 |
99 | # ------------------------------------------------------------------------------
100 | # | Cookie setting from iframes |
101 | # ------------------------------------------------------------------------------
102 |
103 | # Allow cookies to be set from iframes in IE.
104 |
105 | #
106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
107 | #
108 |
109 | # ------------------------------------------------------------------------------
110 | # | Screen flicker |
111 | # ------------------------------------------------------------------------------
112 |
113 | # Stop screen flicker in IE on CSS rollovers (this only works in
114 | # combination with the `ExpiresByType` directives for images from below).
115 |
116 | # BrowserMatch "MSIE" brokenvary=1
117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
118 | # BrowserMatch "Opera" !brokenvary
119 | # SetEnvIf brokenvary 1 force-no-vary
120 |
121 |
122 | # ##############################################################################
123 | # # MIME TYPES AND ENCODING #
124 | # ##############################################################################
125 |
126 | # ------------------------------------------------------------------------------
127 | # | Proper MIME types for all files |
128 | # ------------------------------------------------------------------------------
129 |
130 |
131 |
132 | # Audio
133 | AddType audio/mp4 m4a f4a f4b
134 | AddType audio/ogg oga ogg
135 |
136 | # JavaScript
137 | # Normalize to standard type (it's sniffed in IE anyways):
138 | # http://tools.ietf.org/html/rfc4329#section-7.2
139 | AddType application/javascript js jsonp
140 | AddType application/json json
141 |
142 | # Video
143 | AddType video/mp4 mp4 m4v f4v f4p
144 | AddType video/ogg ogv
145 | AddType video/webm webm
146 | AddType video/x-flv flv
147 |
148 | # Web fonts
149 | AddType application/font-woff woff
150 | AddType application/vnd.ms-fontobject eot
151 |
152 | # Browsers usually ignore the font MIME types and sniff the content,
153 | # however, Chrome shows a warning if other MIME types are used for the
154 | # following fonts.
155 | AddType application/x-font-ttf ttc ttf
156 | AddType font/opentype otf
157 |
158 | # Make SVGZ fonts work on iPad:
159 | # https://twitter.com/FontSquirrel/status/14855840545
160 | AddType image/svg+xml svg svgz
161 | AddEncoding gzip svgz
162 |
163 | # Other
164 | AddType application/octet-stream safariextz
165 | AddType application/x-chrome-extension crx
166 | AddType application/x-opera-extension oex
167 | AddType application/x-shockwave-flash swf
168 | AddType application/x-web-app-manifest+json webapp
169 | AddType application/x-xpinstall xpi
170 | AddType application/xml atom rdf rss xml
171 | AddType image/webp webp
172 | AddType image/x-icon ico
173 | AddType text/cache-manifest appcache manifest
174 | AddType text/vtt vtt
175 | AddType text/x-component htc
176 | AddType text/x-vcard vcf
177 |
178 |
179 |
180 | # ------------------------------------------------------------------------------
181 | # | UTF-8 encoding |
182 | # ------------------------------------------------------------------------------
183 |
184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
185 | AddDefaultCharset utf-8
186 |
187 | # Force UTF-8 for certain file formats.
188 |
189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
190 |
191 |
192 |
193 | # ##############################################################################
194 | # # URL REWRITES #
195 | # ##############################################################################
196 |
197 | # ------------------------------------------------------------------------------
198 | # | Rewrite engine |
199 | # ------------------------------------------------------------------------------
200 |
201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is
202 | # necessary for the following directives to work.
203 |
204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to
205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
207 |
208 | # Also, some cloud hosting services require `RewriteBase` to be set:
209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
210 |
211 |
212 | Options +FollowSymlinks
213 | # Options +SymLinksIfOwnerMatch
214 | RewriteEngine On
215 | # RewriteBase /
216 |
217 |
218 | # ------------------------------------------------------------------------------
219 | # | Suppressing / Forcing the "www." at the beginning of URLs |
220 | # ------------------------------------------------------------------------------
221 |
222 | # The same content should never be available under two different URLs especially
223 | # not with and without "www." at the beginning. This can cause SEO problems
224 | # (duplicate content), therefore, you should choose one of the alternatives and
225 | # redirect the other one.
226 |
227 | # By default option 1 (no "www.") is activated:
228 | # http://no-www.org/faq.php?q=class_b
229 |
230 | # If you'd prefer to use option 2, just comment out all the lines from option 1
231 | # and uncomment the ones from option 2.
232 |
233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
234 |
235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 |
237 | # Option 1: rewrite www.example.com → example.com
238 |
239 |
240 | RewriteCond %{HTTPS} !=on
241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
243 |
244 |
245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 |
247 | # Option 2: rewrite example.com → www.example.com
248 |
249 | # Be aware that the following might not be a good idea if you use "real"
250 | # subdomains for certain parts of your website.
251 |
252 | #
253 | # RewriteCond %{HTTPS} !=on
254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
256 | #
257 |
258 |
259 | # ##############################################################################
260 | # # SECURITY #
261 | # ##############################################################################
262 |
263 | # ------------------------------------------------------------------------------
264 | # | Content Security Policy (CSP) |
265 | # ------------------------------------------------------------------------------
266 |
267 | # You can mitigate the risk of cross-site scripting and other content-injection
268 | # attacks by setting a Content Security Policy which whitelists trusted sources
269 | # of content for your site.
270 |
271 | # The example header below allows ONLY scripts that are loaded from the current
272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't
273 | # work as-is for your site!
274 |
275 | # To get all the details you'll need to craft a reasonable policy for your site,
276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
277 | # see the specification: http://w3.org/TR/CSP).
278 |
279 | #
280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
281 | #
282 | # Header unset Content-Security-Policy
283 | #
284 | #
285 |
286 | # ------------------------------------------------------------------------------
287 | # | File access |
288 | # ------------------------------------------------------------------------------
289 |
290 | # Block access to directories without a default document.
291 | # Usually you should leave this uncommented because you shouldn't allow anyone
292 | # to surf through every directory on your server (which may includes rather
293 | # private places like the CMS's directories).
294 |
295 |
296 | Options -Indexes
297 |
298 |
299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 | # Block access to hidden files and directories.
302 | # This includes directories used by version control systems such as Git and SVN.
303 |
304 |
305 | RewriteCond %{SCRIPT_FILENAME} -d [OR]
306 | RewriteCond %{SCRIPT_FILENAME} -f
307 | RewriteRule "(^|/)\." - [F]
308 |
309 |
310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311 |
312 | # Block access to backup and source files.
313 | # These files may be left by some text editors and can pose a great security
314 | # danger when anyone has access to them.
315 |
316 |
317 | Order allow,deny
318 | Deny from all
319 | Satisfy All
320 |
321 |
322 | # ------------------------------------------------------------------------------
323 | # | Secure Sockets Layer (SSL) |
324 | # ------------------------------------------------------------------------------
325 |
326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
327 | # prevent `https://www.example.com` when your certificate only allows
328 | # `https://secure.example.com`.
329 |
330 | #
331 | # RewriteCond %{SERVER_PORT} !^443
332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
333 | #
334 |
335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336 |
337 | # Force client-side SSL redirection.
338 |
339 | # If a user types "example.com" in his browser, the above rule will redirect him
340 | # to the secure version of the site. That still leaves a window of opportunity
341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the
342 | # request. The following header ensures that browser will ONLY connect to your
343 | # server via HTTPS, regardless of what the users type in the address bar.
344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
345 |
346 | #
347 | # Header set Strict-Transport-Security max-age=16070400;
348 | #
349 |
350 | # ------------------------------------------------------------------------------
351 | # | Server software information |
352 | # ------------------------------------------------------------------------------
353 |
354 | # Avoid displaying the exact Apache version number, the description of the
355 | # generic OS-type and the information about Apache's compiled-in modules.
356 |
357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
358 |
359 | # ServerTokens Prod
360 |
361 |
362 | # ##############################################################################
363 | # # WEB PERFORMANCE #
364 | # ##############################################################################
365 |
366 | # ------------------------------------------------------------------------------
367 | # | Compression |
368 | # ------------------------------------------------------------------------------
369 |
370 |
371 |
372 | # Force compression for mangled headers.
373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
374 |
375 |
376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
378 |
379 |
380 |
381 | # Compress all output labeled with one of the following MIME-types
382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
383 | # and can remove the `` and ` ` lines
384 | # as `AddOutputFilterByType` is still in the core directives).
385 |
386 | AddOutputFilterByType DEFLATE application/atom+xml \
387 | application/javascript \
388 | application/json \
389 | application/rss+xml \
390 | application/vnd.ms-fontobject \
391 | application/x-font-ttf \
392 | application/x-web-app-manifest+json \
393 | application/xhtml+xml \
394 | application/xml \
395 | font/opentype \
396 | image/svg+xml \
397 | image/x-icon \
398 | text/css \
399 | text/html \
400 | text/plain \
401 | text/x-component \
402 | text/xml
403 |
404 |
405 |
406 |
407 | # ------------------------------------------------------------------------------
408 | # | Content transformations |
409 | # ------------------------------------------------------------------------------
410 |
411 | # Prevent some of the mobile network providers from modifying the content of
412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
413 |
414 | #
415 | # Header set Cache-Control "no-transform"
416 | #
417 |
418 | # ------------------------------------------------------------------------------
419 | # | ETag removal |
420 | # ------------------------------------------------------------------------------
421 |
422 | # Since we're sending far-future expires headers (see below), ETags can
423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags.
424 |
425 | # `FileETag None` is not enough for every server.
426 |
427 | Header unset ETag
428 |
429 |
430 | FileETag None
431 |
432 | # ------------------------------------------------------------------------------
433 | # | Expires headers (for better cache control) |
434 | # ------------------------------------------------------------------------------
435 |
436 | # The following expires headers are set pretty far in the future. If you don't
437 | # control versioning with filename-based cache busting, consider lowering the
438 | # cache time for resources like CSS and JS to something like 1 week.
439 |
440 |
441 |
442 | ExpiresActive on
443 | ExpiresDefault "access plus 1 month"
444 |
445 | # CSS
446 | ExpiresByType text/css "access plus 1 year"
447 |
448 | # Data interchange
449 | ExpiresByType application/json "access plus 0 seconds"
450 | ExpiresByType application/xml "access plus 0 seconds"
451 | ExpiresByType text/xml "access plus 0 seconds"
452 |
453 | # Favicon (cannot be renamed!)
454 | ExpiresByType image/x-icon "access plus 1 week"
455 |
456 | # HTML components (HTCs)
457 | ExpiresByType text/x-component "access plus 1 month"
458 |
459 | # HTML
460 | ExpiresByType text/html "access plus 0 seconds"
461 |
462 | # JavaScript
463 | ExpiresByType application/javascript "access plus 1 year"
464 |
465 | # Manifest files
466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
467 | ExpiresByType text/cache-manifest "access plus 0 seconds"
468 |
469 | # Media
470 | ExpiresByType audio/ogg "access plus 1 month"
471 | ExpiresByType image/gif "access plus 1 month"
472 | ExpiresByType image/jpeg "access plus 1 month"
473 | ExpiresByType image/png "access plus 1 month"
474 | ExpiresByType video/mp4 "access plus 1 month"
475 | ExpiresByType video/ogg "access plus 1 month"
476 | ExpiresByType video/webm "access plus 1 month"
477 |
478 | # Web feeds
479 | ExpiresByType application/atom+xml "access plus 1 hour"
480 | ExpiresByType application/rss+xml "access plus 1 hour"
481 |
482 | # Web fonts
483 | ExpiresByType application/font-woff "access plus 1 month"
484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
485 | ExpiresByType application/x-font-ttf "access plus 1 month"
486 | ExpiresByType font/opentype "access plus 1 month"
487 | ExpiresByType image/svg+xml "access plus 1 month"
488 |
489 |
490 |
491 | # ------------------------------------------------------------------------------
492 | # | Filename-based cache busting |
493 | # ------------------------------------------------------------------------------
494 |
495 | # If you're not using a build process to manage your filename version revving,
496 | # you might want to consider enabling the following directives to route all
497 | # requests such as `/css/style.12345.css` to `/css/style.css`.
498 |
499 | # To understand why this is important and a better idea than `*.css?v231`, read:
500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
501 |
502 | #
503 | # RewriteCond %{REQUEST_FILENAME} !-f
504 | # RewriteCond %{REQUEST_FILENAME} !-d
505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
506 | #
507 |
508 | # ------------------------------------------------------------------------------
509 | # | File concatenation |
510 | # ------------------------------------------------------------------------------
511 |
512 | # Allow concatenation from within specific CSS and JS files, e.g.:
513 | # Inside of `script.combined.js` you could have
514 | #
515 | #
516 | # and they would be included into this single file.
517 |
518 | #
519 | #
520 | # Options +Includes
521 | # AddOutputFilterByType INCLUDES application/javascript application/json
522 | # SetOutputFilter INCLUDES
523 | #
524 | #
525 | # Options +Includes
526 | # AddOutputFilterByType INCLUDES text/css
527 | # SetOutputFilter INCLUDES
528 | #
529 | #
530 |
531 | # ------------------------------------------------------------------------------
532 | # | Persistent connections |
533 | # ------------------------------------------------------------------------------
534 |
535 | # Allow multiple requests to be sent over the same TCP connection:
536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
537 |
538 | # Enable if you serve a lot of static content but, be aware of the
539 | # possible disadvantages!
540 |
541 | #
542 | # Header set Connection Keep-Alive
543 | #
544 |
--------------------------------------------------------------------------------
/client/app/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Page Not Found :(
6 |
141 |
142 |
143 |
144 |
Not found :(
145 |
Sorry, but the page you were trying to view does not exist.
146 |
It looks like this was the result of either:
147 |
148 | a mistyped address
149 | an out-of-date link
150 |
151 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/client/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/client/app/favicon.ico
--------------------------------------------------------------------------------
/client/app/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/client/app/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/client/app/fonts/glyphicons-halflings-regular.svg:
--------------------------------------------------------------------------------
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 |
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 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/client/app/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/client/app/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/client/app/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/client/app/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/client/app/images/yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beeman/angular-restangular-crud/33704525cb4adf267072906581ce5d3477e6871a/client/app/images/yeoman.png
--------------------------------------------------------------------------------
/client/app/index.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 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/client/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/client/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var app = angular.module('scaffoldApp', [
4 | 'ngCookies',
5 | 'ngResource',
6 | 'ngSanitize',
7 | 'restangular',
8 | 'ui.router'
9 | ]);
10 |
11 | app.config(function (RestangularProvider) {
12 | RestangularProvider.setBaseUrl('http://localhost:3000/api');
13 | });
14 |
15 |
16 | app.config(['$stateProvider', '$urlRouterProvider',
17 | function ($stateProvider, $urlRouterProvider) {
18 |
19 | $urlRouterProvider.otherwise('/');
20 |
21 | $stateProvider
22 | .state('home', {
23 | url: '/',
24 | templateUrl: 'views/main.html',
25 | controller: 'MainCtrl'
26 | });
27 |
28 |
29 | }
30 | ]);
--------------------------------------------------------------------------------
/client/app/scripts/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('scaffoldApp').controller('MainCtrl', function ($scope) {
4 | $scope.hello = 'from AngularJS';
5 | });
--------------------------------------------------------------------------------
/client/app/scripts/controllers/notes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var app = angular.module('scaffoldApp');
4 |
5 | /* Define the Repository that interfaces with Restangular */
6 | app.factory('NoteRepository', ['Restangular', 'AbstractRepository',
7 | function (restangular, AbstractRepository) {
8 |
9 | function NoteRepository() {
10 | AbstractRepository.call(this, restangular, 'notes');
11 | }
12 |
13 | AbstractRepository.extend(NoteRepository);
14 | return new NoteRepository();
15 | }
16 | ]);
17 |
18 | /* Here the controllers are defines */
19 | app.controller('NotesListCtrl', function ($scope, NoteRepository) {
20 | $scope.notes = NoteRepository.getList();
21 | $scope.delete = function (data) {
22 | if(window.confirm('Are you sure?')) {
23 | NoteRepository.remove(data).then(function () {
24 | $scope.notes = NoteRepository.getList();
25 | });
26 | }
27 | };
28 | });
29 |
30 | app.controller('NotesItemCtrl', function ($scope, $stateParams, NoteRepository) {
31 | $scope.note = NoteRepository.get($stateParams.id).then(function (data) {
32 | $scope.note = data;
33 | });
34 | });
35 |
36 | app.controller('NotesEditCtrl', function ($scope, $stateParams, $location, NoteRepository) {
37 | $scope.note = NoteRepository.get($stateParams.id).then(function (data) {
38 | $scope.note = data;
39 | });
40 | $scope.save = function () {
41 | $scope.note.put().then(function () {
42 | $location.path('/notes/' + $stateParams.id);
43 | });
44 | };
45 | });
46 |
47 | app.controller('NotesAddCtrl', function ($scope, $location, NoteRepository) {
48 | $scope.save = function () {
49 | NoteRepository.create($scope.note).then(function () {
50 | $location.path('/notes');
51 | });
52 | };
53 | });
54 |
55 | /* Below are the states that are used */
56 | app.config(['$stateProvider',
57 | function ($stateProvider) {
58 |
59 | $stateProvider
60 | .state('notes', {
61 | abstract: true,
62 | url: '/notes',
63 | templateUrl: 'views/notes/main.html'
64 | })
65 |
66 | .state('notes.list', {
67 | url: '',
68 | templateUrl: 'views/notes/list.html',
69 | controller: 'NotesListCtrl'
70 | })
71 |
72 | .state('notes.add', {
73 | url: '/add',
74 | templateUrl: 'views/notes/edit.html',
75 | controller: 'NotesAddCtrl'
76 | })
77 |
78 | .state('notes.edit', {
79 | url: '/edit/{id}',
80 | templateUrl: 'views/notes/edit.html',
81 | controller: 'NotesEditCtrl'
82 | })
83 |
84 | .state('notes.item', {
85 | url: '/{id}',
86 | templateUrl: 'views/notes/item.html',
87 | controller: 'NotesItemCtrl'
88 | });
89 | }
90 | ]);
--------------------------------------------------------------------------------
/client/app/scripts/factory.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var app = angular.module('scaffoldApp');
4 |
5 | app.factory('AbstractRepository', [
6 |
7 | function () {
8 |
9 | function AbstractRepository(restangular, route) {
10 | this.restangular = restangular;
11 | this.route = route;
12 | }
13 |
14 | AbstractRepository.prototype = {
15 | getList: function (params) {
16 | return this.restangular.all(this.route).getList(params).$object;
17 | },
18 | get: function (id) {
19 | return this.restangular.one(this.route, id).get();
20 | },
21 | getView: function (id) {
22 | return this.restangular.one(this.route, id).one(this.route + 'view').get();
23 | },
24 | update: function (updatedResource) {
25 | return updatedResource.put().$object;
26 | },
27 | create: function (newResource) {
28 | return this.restangular.all(this.route).post(newResource);
29 | },
30 | remove: function (object) {
31 | return this.restangular.one(this.route, object.id).remove();
32 | }
33 | };
34 |
35 | AbstractRepository.extend = function (repository) {
36 | repository.prototype = Object.create(AbstractRepository.prototype);
37 | repository.prototype.constructor = repository;
38 | };
39 |
40 | return AbstractRepository;
41 | }
42 | ]);
43 |
--------------------------------------------------------------------------------
/client/app/styles/main.css:
--------------------------------------------------------------------------------
1 | /* Space out content a bit */
2 | body {
3 | padding-top: 20px;
4 | padding-bottom: 20px;
5 | }
6 |
7 | /* Everything but the jumbotron gets side spacing for mobile first views */
8 | .header,
9 | .marketing,
10 | .footer {
11 | padding-left: 15px;
12 | padding-right: 15px;
13 | }
14 |
15 | /* Custom page header */
16 | .header {
17 | border-bottom: 1px solid #e5e5e5;
18 | }
19 | /* Make the masthead heading the same height as the navigation */
20 | .header h3 {
21 | margin-top: 0;
22 | margin-bottom: 0;
23 | line-height: 40px;
24 | padding-bottom: 19px;
25 | }
26 |
27 | /* Custom page footer */
28 | .footer {
29 | padding-top: 19px;
30 | color: #777;
31 | border-top: 1px solid #e5e5e5;
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/client/app/views/main.html:
--------------------------------------------------------------------------------
1 | Hello
2 | {{hello}}
3 |
4 |
--------------------------------------------------------------------------------
/client/app/views/notes/edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{note.name}}
22 |
23 |
24 |
{{note.content}}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/client/app/views/notes/item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{note.name}}
13 |
14 |
15 |
{{note.content}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/client/app/views/notes/list.html:
--------------------------------------------------------------------------------
1 |
2 | Notes
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
22 |
23 |
{{note.content}}
24 |
25 |
26 |
27 |
28 |
33 |
--------------------------------------------------------------------------------
/client/app/views/notes/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/client/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scaffold",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "1.2.6",
6 | "json3": "~3.2.6",
7 | "es5-shim": "~2.1.0",
8 | "jquery": "~1.10.2",
9 | "bootstrap": "~3.0.3",
10 | "angular-resource": "1.2.6",
11 | "angular-cookies": "1.2.6",
12 | "angular-sanitize": "1.2.6",
13 | "angular-route": "1.2.6",
14 | "restangular": "~1.3.1",
15 | "angular-ui-router": "~0.2.10"
16 | },
17 | "devDependencies": {
18 | "angular-mocks": "1.2.6",
19 | "angular-scenario": "1.2.6"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scaffold",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "grunt": "~0.4.1",
7 | "grunt-autoprefixer": "~0.4.0",
8 | "grunt-bower-install": "~0.7.0",
9 | "grunt-concurrent": "~0.4.1",
10 | "grunt-contrib-clean": "~0.5.0",
11 | "grunt-contrib-coffee": "~0.7.0",
12 | "grunt-contrib-compass": "~0.6.0",
13 | "grunt-contrib-concat": "~0.3.0",
14 | "grunt-contrib-connect": "~0.5.0",
15 | "grunt-contrib-copy": "~0.4.1",
16 | "grunt-contrib-cssmin": "~0.7.0",
17 | "grunt-contrib-htmlmin": "~0.1.3",
18 | "grunt-contrib-imagemin": "~0.3.0",
19 | "grunt-contrib-jshint": "~0.7.1",
20 | "grunt-contrib-uglify": "~0.2.0",
21 | "grunt-contrib-watch": "~0.5.2",
22 | "grunt-google-cdn": "~0.2.0",
23 | "grunt-newer": "~0.5.4",
24 | "grunt-ngmin": "~0.0.2",
25 | "grunt-rev": "~0.1.0",
26 | "grunt-svgmin": "~0.2.0",
27 | "grunt-usemin": "~2.0.0",
28 | "jshint-stylish": "~0.1.3",
29 | "load-grunt-tasks": "~0.2.0",
30 | "time-grunt": "~0.2.1"
31 | },
32 | "engines": {
33 | "node": ">=0.8.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/client/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "jasmine": false,
33 | "spyOn": false
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/client/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/client/test/spec/controllers/items.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: ItemsCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('scaffoldApp'));
7 |
8 | var ItemsCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | ItemsCtrl = $controller('ItemsCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/client/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('scaffoldApp'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------