├── .gitignore
├── LICENSE
├── README.md
├── cli
├── cli.js
└── templates
│ ├── component
│ ├── directive.js.template
│ ├── style.scss.template
│ └── template.html.template
│ └── page
│ ├── controller.js.template
│ ├── style.scss.template
│ └── template.html.template
├── core
├── generate.js
├── initializers
│ └── angular_spa_initializer.js
└── module.js
├── index.js
├── package.json
└── src
├── angular
├── app
│ ├── components
│ │ └── test-component
│ │ │ ├── directive.js
│ │ │ ├── style.scss
│ │ │ └── template.html
│ ├── factories
│ │ └── api.js
│ ├── pages
│ │ └── root
│ │ │ ├── controller.js
│ │ │ ├── index
│ │ │ ├── controller.js
│ │ │ ├── style.scss
│ │ │ └── template.html
│ │ │ ├── style.scss
│ │ │ └── template.html
│ └── style
│ │ ├── base.scss
│ │ └── import.scss
├── application.js
├── index.html
├── router.js
└── vendor
│ ├── script
│ └── sample.js
│ └── style
│ └── sample.css
└── app
├── controllers
└── index_controller.js
└── router.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Keith William Horwood
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nodal-angular
2 |
3 | Welcome to the Angular SPA plugin for [Nodal](https://github.com/keithwhor/nodal)!
4 |
5 | This module contains some simple CLI tools for working with a new Angular project,
6 | as well as an initializer that compiles your Angular SPA for you.
7 |
8 | ## Installation
9 |
10 | Installation is simple.
11 |
12 | 1. Begin a new Nodal project with `nodal new`
13 | 2. In your new project directory, type `npm install nodal-angular --save`
14 | 3. Now type `nodal-angular init`
15 | 4. Next, go to `app/app.js` and find the following line:
16 |
17 | ```javascript
18 | /* Import Initializers */
19 | const StaticAssetInitializer = Nodal.require('initializers/static_asset_initializer.js');
20 | ```
21 |
22 | Add the following line:
23 |
24 | ```javascript
25 | const AngularInitializer = require('nodal-angular').Initializer;
26 | ```
27 |
28 | 5. In the same file, find:
29 |
30 | ```javascript
31 | /* Initializers */
32 | this.initializers.use(StaticAssetInitializer);
33 | ```
34 |
35 | And change it to:
36 |
37 | ```javascript
38 | /* Initializers */
39 | this.initializers.use(AngularInitializer);
40 | this.initializers.use(StaticAssetInitializer);
41 | ```
42 |
43 | Make sure you assign your AngularInitializer *first*! It copies files to be
44 | served by your StaticAssetInitializer.
45 |
46 | All Done!
47 |
--------------------------------------------------------------------------------
/cli/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | (function() {
4 |
5 | 'use strict';
6 |
7 | const colors = require('colors/safe');
8 | const fs = require('fs-extra');
9 | const inflect = require('i')();
10 | const dot = require('dot');
11 | dot.templateSettings.strip = false;
12 |
13 | let command = process.argv.slice(2, 3).pop();
14 |
15 | command = command ? command : '_';
16 | command = {name: command.split(':')[0], value: command.split(':')[1] || '_'};
17 |
18 | let args = [];
19 | let flags = {};
20 |
21 | process.argv.slice(3).forEach(function(v) {
22 | let values = v.split(':');
23 | if (v.substr(0, 2) === '--') {
24 | values[0] = values[0].substr(2);
25 | flags[values[0]] = values[1];
26 | } else {
27 | args.push(values);
28 | }
29 | });
30 |
31 | let fnError = function(str) {
32 | console.error(colors.red.bold('Error: ') + str);
33 | process.exit(1);
34 | };
35 | if (!fs.existsSync(process.cwd() + '/.nodal')) {
36 |
37 | fnError('Cannot use nodal-ng: No Nodal project in this directory.');
38 |
39 | }
40 |
41 | let commands = {
42 | init: {
43 | _: (args, flags) => {
44 |
45 | if (fs.existsSync(process.cwd() + '/angular')) {
46 | fnError('Cannot initialize angular project --- seems you already have an angular directory present!');
47 | }
48 |
49 | fs.copySync(__dirname + '/../src/angular', process.cwd() + '/angular');
50 | fs.copySync(__dirname + '/../src/app/router.js', process.cwd() + '/app/router.js', {clobber: true});
51 | fs.copySync(__dirname + '/../src/app/controllers/index_controller.js', process.cwd() + '/app/controllers/index_controller.js', {clobber: true});
52 |
53 | console.log(colors.green.bold('Complete: ') + 'Angular SPA initialized successfully! Please make sure to include the angular initializer in app.js.');
54 |
55 | }
56 | },
57 | g: {
58 | page: (args, flags) => {
59 |
60 | let fArg = args[0] && args[0][0] || '';
61 |
62 | if (!fArg) {
63 | fnError('Must supply a page path.');
64 | }
65 |
66 | let path = ['angular', 'app', 'pages'].concat(fArg.split('/'))
67 | .map(v => inflect.underscore(v))
68 | .map(v => inflect.dasherize(v));
69 |
70 | let pagePath = process.cwd() + '/' + path.join('/');
71 |
72 | if (fs.existsSync(pagePath)) {
73 | fnError('This page already exists.');
74 | }
75 |
76 | for (let i = 0; i < path.length; i++) {
77 | let dirPath = process.cwd() + '/' + path.slice(0, i + 1).join('/');
78 | if (!fs.existsSync(dirPath)) {
79 | fs.mkdirSync(dirPath);
80 | }
81 | }
82 |
83 | let data = {};
84 | data.controllerName = fArg.split('/')
85 | .map(v => inflect.underscore(v))
86 | .map(v => inflect.camelize(v))
87 | .join('') + 'Controller';
88 |
89 | [
90 | 'controller.js',
91 | 'style.scss',
92 | 'template.html'
93 | ].forEach(f => {
94 |
95 | let template = dot.template(fs.readFileSync(__dirname + `/templates/page/${f}.template`).toString());
96 | fs.writeFileSync([pagePath, f].join('/'), template(data));
97 |
98 | console.log(colors.green.bold('Create: ') + [pagePath, f].join('/'));
99 |
100 | });
101 |
102 | },
103 | component: (args, flags) => {
104 |
105 | let fArg = args[0] && args[0][0] || '';
106 |
107 | if (!fArg) {
108 | fnError('Must supply a component path.');
109 | }
110 |
111 | if (fArg.indexOf('/') !== -1) {
112 | fnError('Components can not be nested within other component directories');
113 | }
114 |
115 | let data = {};
116 | data.componentName = inflect.camelize(inflect.underscore(fArg));
117 | data.componentNameDash = inflect.dasherize(inflect.underscore(fArg));
118 |
119 | let path = ['angular', 'app', 'components', data.componentNameDash];
120 | let componentPath = process.cwd() + '/' + path.join('/');
121 |
122 | if (fs.existsSync(componentPath)) {
123 | fnError('This component already exists.');
124 | }
125 |
126 | for (let i = 0; i < path.length; i++) {
127 | let dirPath = process.cwd() + '/' + path.slice(0, i + 1).join('/');
128 | if (!fs.existsSync(dirPath)) {
129 | fs.mkdirSync(dirPath);
130 | }
131 | }
132 |
133 | [
134 | 'directive.js',
135 | 'style.scss',
136 | 'template.html'
137 | ].forEach(f => {
138 |
139 | let template = dot.template(fs.readFileSync(__dirname + `/templates/component/${f}.template`).toString());
140 | fs.writeFileSync([componentPath, f].join('/'), template(data));
141 |
142 | console.log(colors.green.bold('Create: ') + [componentPath, f].join('/'));
143 |
144 | });
145 |
146 | }
147 | }
148 | };
149 |
150 | let execFn = commands[command.name] &&
151 | commands[command.name][command.value] ||
152 | () => { fnError('Invalid command'); };
153 |
154 | execFn(args, flags);
155 |
156 | process.exit(0);
157 |
158 | })();
159 |
--------------------------------------------------------------------------------
/cli/templates/component/directive.js.template:
--------------------------------------------------------------------------------
1 | app.directive('{{= it.componentName }}', function() {
2 |
3 | return {
4 | templateUrl: 'components/{{= it.componentNameDash }}/template.html'
5 | };
6 |
7 | });
8 |
--------------------------------------------------------------------------------
/cli/templates/component/style.scss.template:
--------------------------------------------------------------------------------
1 | {{= it.componentNameDash }} {
2 |
3 | /* style your components here */
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/cli/templates/component/template.html.template:
--------------------------------------------------------------------------------
1 |
2 | New Component: {{= it.componentName }}
3 |
4 |
--------------------------------------------------------------------------------
/cli/templates/page/controller.js.template:
--------------------------------------------------------------------------------
1 | app.controller('{{= it.controllerName }}', ['$scope', 'API', function($scope, API) {
2 |
3 | // Controller Code
4 |
5 | }]);
6 |
--------------------------------------------------------------------------------
/cli/templates/page/style.scss.template:
--------------------------------------------------------------------------------
1 | [ng-controller="{{= it.controllerName }}"] {
2 |
3 | /* style your controller here */
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/cli/templates/page/template.html.template:
--------------------------------------------------------------------------------
1 |
2 | My New Page ({{= it.controllerName }})
3 |
4 |
--------------------------------------------------------------------------------
/core/generate.js:
--------------------------------------------------------------------------------
1 | module.exports = (() => {
2 |
3 | const AngularSPAInitializer = require('./initializers/angular_spa_initializer.js');
4 | const html = AngularSPAInitializer.prototype.compileHTML(AngularSPAInitializer.PATHS.script);
5 |
6 | return (globals) => {
7 | globals = globals && typeof globals === 'object' ? globals : {};
8 | return `${html}`;
9 | };
10 |
11 | })();
12 |
--------------------------------------------------------------------------------
/core/initializers/angular_spa_initializer.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 |
3 | 'use strict';
4 |
5 | const fs = require('fs-extra');
6 | const sass = require('node-sass');
7 |
8 | class AngularSPAInitializer {
9 |
10 | exec(callback) {
11 |
12 | // Compile JavaScript, with minify flag
13 | let script = this.compileJavaScript(process.env.NODE_ENV === 'production');
14 | fs.outputFileSync(`static/${this.constructor.PATHS.script}`, script);
15 |
16 | // Compile css
17 | let css = this.compileCSS();
18 | fs.outputFileSync(`static/${this.constructor.PATHS.css}`, css);
19 |
20 | // Copy all vendor references
21 | fs.copySync('angular/vendor', `static/${this.constructor.PATHS.vendor}`);
22 |
23 | callback(null);
24 |
25 | }
26 |
27 | compileJavaScript(minify) {
28 |
29 | let files = [
30 | fs.readFileSync('angular/application.js').toString(),
31 | fs.readFileSync('angular/router.js').toString()
32 | ];
33 |
34 | this.readDir(
35 | 'angular/app/factories',
36 | /^.*\.js$/,
37 | (path, filename) => files.push(fs.readFileSync(path).toString())
38 | );
39 |
40 | this.readDir(
41 | 'angular/app/pages',
42 | /^.*\.js$/,
43 | (path, filename) => files.push(fs.readFileSync(path).toString())
44 | );
45 |
46 | this.readDir(
47 | 'angular/app/components',
48 | /^.*\.js$/,
49 | (path, filename) => files.push(fs.readFileSync(path).toString())
50 | );
51 |
52 | return files.join('');
53 |
54 | }
55 |
56 | compileCSS() {
57 |
58 | let css = [];
59 |
60 | this.readDir(
61 | 'angular/app/pages',
62 | /^.*\.scss$/,
63 | (path, filename) => css.push(fs.readFileSync(path).toString())
64 | );
65 |
66 | this.readDir(
67 | 'angular/app/components',
68 | /^.*\.scss$/,
69 | (path, filename) => css.push(fs.readFileSync(path).toString())
70 | );
71 |
72 | let result = sass.renderSync({
73 | data: `@import 'import'; ${css.join('')}`,
74 | outputStyle: 'compressed',
75 | includePaths: ['angular/app/style']
76 | });
77 |
78 | return result.css.toString();
79 |
80 | };
81 |
82 | compileHTML(scriptPath) {
83 |
84 | let templates = [
85 | fs.readFileSync('angular/index.html').toString()
86 | ];
87 |
88 | this.readDir(
89 | 'angular/app/pages',
90 | /^.*\.html$/,
91 | (path, filename) => {
92 | templates.push(
93 | `'
96 | );
97 | }
98 | );
99 |
100 | this.readDir(
101 | 'angular/app/components',
102 | /^.*\.html$/,
103 | (path, filename) => {
104 | templates.push(
105 | `'
108 | );
109 | }
110 | );
111 |
112 | templates.push(``);
113 |
114 | return templates.join('');
115 |
116 | }
117 |
118 | readDir(dirname, match, fnMatch) {
119 |
120 | let cwd = process.cwd();
121 |
122 | let files = fs.readdirSync([cwd, dirname].join('/'));
123 |
124 | files.forEach(filename => {
125 |
126 | let relPath = [dirname, filename].join('/');
127 | let fullPath = [cwd, relPath].join('/');
128 |
129 | let stat = fs.statSync(fullPath);
130 |
131 | if (stat.isDirectory()) {
132 | this.readDir(relPath, match, fnMatch);
133 | return;
134 | }
135 |
136 | if (filename.match(match)) {
137 |
138 | fnMatch(relPath, filename, fullPath);
139 |
140 | }
141 |
142 | });
143 |
144 | }
145 |
146 | }
147 |
148 | AngularSPAInitializer.PATHS = {
149 | script: 'compiled/app.js',
150 | css: 'compiled/style.css',
151 | vendor: 'compiled/vendor'
152 | };
153 |
154 | return AngularSPAInitializer;
155 |
156 | })();
157 |
--------------------------------------------------------------------------------
/core/module.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 |
3 | return {
4 | Initializer: require('./initializers/angular_spa_initializer.js'),
5 | generate: require('./generate.js')
6 | };
7 |
8 | })();
9 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keithwhor/nodal-angular/a68103aef8fe193e0b581556235fd317d2102f35/index.js
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodal-angular",
3 | "version": "0.0.4",
4 | "description": "Angular SPA tools for Nodal",
5 | "author": "Keith Horwood",
6 | "main": "core/module.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "nodal",
12 | "angular",
13 | "spa"
14 | ],
15 | "dependencies": {
16 | "colors": "^1.1.2",
17 | "dot": "^1.0.3",
18 | "fs-extra": "^0.26.3",
19 | "i": "^0.3.3",
20 | "node-sass": "^3.4.2"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/keithwhor/nodal-angular.git"
25 | },
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/keithwhor/nodal-angular/issues"
29 | },
30 | "bin": {
31 | "nodal-angular": "cli/cli.js"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/angular/app/components/test-component/directive.js:
--------------------------------------------------------------------------------
1 | app.directive('testComponent', function() {
2 |
3 | return {
4 | templateUrl: 'components/test-component/template.html'
5 | };
6 |
7 | });
8 |
--------------------------------------------------------------------------------
/src/angular/app/components/test-component/style.scss:
--------------------------------------------------------------------------------
1 | test-component {
2 |
3 | color: #000000;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/angular/app/components/test-component/template.html:
--------------------------------------------------------------------------------
1 |
2 | Test Component
3 |
4 |
--------------------------------------------------------------------------------
/src/angular/app/factories/api.js:
--------------------------------------------------------------------------------
1 | app.factory('API', ['$rootScope', function($rootScope) {
2 |
3 | function copyAdd(obj, field, value) {
4 |
5 | var newObj = {};
6 | value && (newObj[field] = value);
7 |
8 | Object.keys(obj).forEach(function(k) {
9 | newObj[k] = obj[k];
10 | });
11 |
12 | return newObj;
13 |
14 | }
15 |
16 | function serializeParameters(obj) {
17 |
18 | var fnConvert = function(keys, isArray, v) {
19 | isArray = ['', '[]'][isArray | 0];
20 | return (keys.length < 2) ? (
21 | [keys[0], isArray, '=', v].join('')
22 | ) : (
23 | [keys[0], '[' + keys.slice(1).join(']['), ']', isArray, '=', v].join('')
24 | );
25 | };
26 |
27 | var fnSerialize = function(keys, key, i) {
28 |
29 | keys = keys.concat([key]);
30 | var datum = obj;
31 |
32 | keys.forEach(function(key) {
33 | datum = datum[key];
34 | });
35 |
36 | if (datum instanceof Date) {
37 |
38 | datum = [datum.getFullYear(), datum.getMonth() + 1, datum.getDate()].join('-');
39 |
40 | }
41 |
42 | if (datum instanceof Array) {
43 |
44 | return datum.map(fnConvert.bind(null, keys, true)).join('&');
45 |
46 | } else if (typeof(datum) === 'object' && datum !== null) {
47 |
48 | return Object.keys(datum).map(fnSerialize.bind(null, keys)).join('&');
49 |
50 | }
51 |
52 | return fnConvert(keys, false, datum);
53 |
54 | };
55 |
56 | return Object.keys(obj).map(fnSerialize.bind(null, [])).join('&');
57 |
58 | }
59 |
60 | function APIConnect(domain) {
61 |
62 | if (domain[domain.length - 1] === '/') {
63 | domain = domain.substr(0, domain.length - 1);
64 | }
65 |
66 | this._domain = domain;
67 | this._accessToken = localStorage.getItem(this._domain + ':accessToken') || undefined;
68 |
69 | }
70 |
71 | APIConnect.prototype.request = function(path) {
72 |
73 | if (path[0] === '/') {
74 | path = path.substr(1);
75 | }
76 |
77 | return new APIRequest(this._domain, path, this._accessToken);
78 |
79 | };
80 |
81 | APIConnect.prototype.open = function(path, params) {
82 |
83 | window.location = [
84 | [this._domain, path].join('/'),
85 | '?',
86 | serializeParameters(copyAdd(params, 'access_token', this._accessToken))
87 | ].join('');
88 |
89 | };
90 |
91 | APIConnect.prototype.setAccessToken = function(accessToken) {
92 | if (accessToken) {
93 | localStorage.setItem(this._domain + ':accessToken', accessToken);
94 | this._accessToken = accessToken
95 | } else {
96 | this.clearAccessToken();
97 | }
98 | };
99 |
100 | APIConnect.prototype.clearAccessToken = function() {
101 | localStorage.removeItem(this._domain + ':accessToken');
102 | this._accessToken = undefined;
103 | };
104 |
105 | function APIRequest(domain, path, accessToken) {
106 | this._url = [domain, path].join('/');
107 | this._accessToken = accessToken || undefined;
108 | }
109 |
110 | APIRequest.prototype.addAccessToken = function(obj) {
111 |
112 | return copyAdd(obj, 'access_token', this._accessToken);
113 |
114 | };
115 |
116 | APIRequest.prototype.addAccessTokenQueryString = function() {
117 |
118 | return (this._accessToken ? '?access_token=' + this._accessToken : '');
119 |
120 | };
121 |
122 | APIRequest.prototype.index = function(params, callback) {
123 |
124 | return new APIXHR(this._url, callback).get(this.addAccessToken(params));
125 |
126 | };
127 |
128 | APIRequest.prototype.show = function(id, params, callback) {
129 |
130 | return new APIXHR(this._url + (id ? '/' + id : ''), callback).get(this.addAccessToken(params));
131 |
132 | };
133 |
134 | APIRequest.prototype.destroy = function(id, params, callback) {
135 |
136 | return new APIXHR(this._url + (id ? '/' + id : ''), callback).del(this.addAccessToken(params));
137 |
138 | };
139 |
140 | APIRequest.prototype.create = function(params, callback) {
141 |
142 | return new APIXHR(this._url + this.addAccessTokenQueryString(), callback).post(params);
143 |
144 | };
145 |
146 | APIRequest.prototype.update = function(id, params, callback) {
147 |
148 | return new APIXHR(this._url + (id ? '/' + id : '') + this.addAccessTokenQueryString(), callback).put(params);
149 |
150 | };
151 |
152 | APIRequest.prototype.upload = function(file, callback) {
153 |
154 | return new APIXHR(this._url + this.addAccessTokenQueryString(), callback).upload(file);
155 |
156 | };
157 |
158 | function APIXHR(url, callback) {
159 |
160 | this._url = url;
161 | this._active = false;
162 | this._complete = false;
163 |
164 | var self = this;
165 | var xhr = new XMLHttpRequest();
166 | this._xhr = xhr;
167 |
168 | var cb = callback;
169 | callback = function() {
170 |
171 | self._complete = true;
172 | cb.apply(this, arguments);
173 | $rootScope.$digest();
174 |
175 | };
176 |
177 | this._callback = callback;
178 |
179 | xhr.addEventListener('readystatechange', function() {
180 |
181 | var obj;
182 |
183 | if (xhr.readyState === 0) {
184 | callback.call(self, new Error('Request aborted'), null, []);
185 | return;
186 | }
187 |
188 | if (xhr.readyState === 4) {
189 |
190 | if (xhr.status === 0) {
191 | callback.call(self, new Error('Request aborted'), null, []);
192 | return;
193 | }
194 |
195 | try {
196 | obj = JSON.parse(xhr.responseText);
197 | } catch(e) {
198 | callback.call(self, new Error('Expected JSON, could not parse response'), null, []);
199 | return;
200 | }
201 |
202 | if (obj.meta && obj.meta.error) {
203 | callback.call(self, obj.meta.error, obj, obj.data || []);
204 | return;
205 | }
206 |
207 | callback.call(self, null, obj, obj.data || []);
208 | return;
209 |
210 | }
211 |
212 | });
213 |
214 | xhr.addEventListener('error', function(err) {
215 |
216 | callback.call(self, err, null, []);
217 |
218 | });
219 |
220 | return this;
221 |
222 | }
223 |
224 | APIXHR.prototype.__checkActiveState__ = function() {
225 | if (this._active) {
226 | throw new Error('APIXHR is already active, can only be aborted.');
227 | }
228 | return true;
229 | };
230 |
231 | APIXHR.prototype.__setActiveState__ = function() {
232 | this._active = true;
233 | };
234 |
235 | APIXHR.prototype.abort = function() {
236 |
237 | if (!this._active) {
238 | throw new Error('Cannot abort APIXHR that is not active');
239 | }
240 |
241 | if (!this._complete) {
242 | this._xhr.abort();
243 | }
244 |
245 | return this;
246 |
247 | };
248 |
249 | APIXHR.prototype.get = function(params) {
250 | this.__checkActiveState__();
251 | var xhr = this._xhr;
252 | xhr.open('GET', [this._url, serializeParameters(params)].join('?'));
253 | xhr.send();
254 | this.__setActiveState__();
255 | return this;
256 | };
257 |
258 | APIXHR.prototype.del = function(params) {
259 | this.__checkActiveState__();
260 | var xhr = this._xhr;
261 | xhr.open('DELETE', [this._url, serializeParameters(params)].join('?'));
262 | xhr.send();
263 | this.__setActiveState__();
264 | return this;
265 | };
266 |
267 | APIXHR.prototype.post = function(params) {
268 | this.__checkActiveState__();
269 | var xhr = this._xhr;
270 | xhr.open('POST', this._url);
271 | xhr.setRequestHeader('Content-Type', 'application/json');
272 | xhr.send(JSON.stringify(params));
273 | this.__setActiveState__();
274 | return this;
275 | };
276 |
277 | APIXHR.prototype.put = function(params) {
278 | this.__checkActiveState__();
279 | var xhr = this._xhr;
280 | xhr.open('PUT', this._url);
281 | xhr.setRequestHeader('Content-Type', 'application/json');
282 | xhr.send(JSON.stringify(params));
283 | this.__setActiveState__();
284 | return this;
285 | };
286 |
287 | APIXHR.prototype.upload = function(file) {
288 | this.__checkActiveState__();
289 | var xhr = this._xhr;
290 | xhr.open('POST', this._url);
291 | xhr.send(file);
292 | this.__setActiveState__();
293 | return this;
294 | };
295 |
296 | return new APIConnect((window.globals && window.globals.api_url) || '');
297 |
298 | }]);
299 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/controller.js:
--------------------------------------------------------------------------------
1 | app.controller('RootController', ['$scope', '$http', function($scope, $http) {
2 |
3 | $scope.greeting = 'Hello, world.';
4 |
5 | }]);
6 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/index/controller.js:
--------------------------------------------------------------------------------
1 | app.controller('RootIndexController', ['$scope', 'API', function ($scope, API) {
2 |
3 | $scope.name = 'Jo';
4 |
5 | }]);
6 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/index/style.scss:
--------------------------------------------------------------------------------
1 | [ng-controller="RootIndexController"] {
2 |
3 | /* style your controller here */
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/index/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello, {{ name }}.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/style.scss:
--------------------------------------------------------------------------------
1 | [ng-controller="RootController"] {
2 |
3 | /* style your controller here */
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/angular/app/pages/root/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ greeting }}
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/angular/app/style/base.scss:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: 'Arial';
3 | margin: 0px;
4 | padding: 0px;
5 | }
6 |
--------------------------------------------------------------------------------
/src/angular/app/style/import.scss:
--------------------------------------------------------------------------------
1 | @import 'base';
2 |
--------------------------------------------------------------------------------
/src/angular/application.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('App', ['ui.router']);
2 |
--------------------------------------------------------------------------------
/src/angular/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Nodal Angular SPA
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 |
--------------------------------------------------------------------------------
/src/angular/router.js:
--------------------------------------------------------------------------------
1 | app.config(function($stateProvider, $locationProvider, $urlRouterProvider) {
2 |
3 | $locationProvider.html5Mode(true);
4 |
5 | $stateProvider
6 |
7 | .state(
8 | 'root',
9 | {
10 | templateUrl: 'pages/root/template.html',
11 | }
12 | )
13 |
14 | .state(
15 | 'root.index',
16 | {
17 | url: '/',
18 | templateUrl: 'pages/root/index/template.html'
19 | }
20 | )
21 |
22 | $urlRouterProvider.otherwise('/');
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/src/angular/vendor/script/sample.js:
--------------------------------------------------------------------------------
1 | console.log('This is a sample vendor script');
2 |
--------------------------------------------------------------------------------
/src/angular/vendor/style/sample.css:
--------------------------------------------------------------------------------
1 | a {
2 | color: #0000ff;
3 | text-decoration: none;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/controllers/index_controller.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 |
3 | 'use strict';
4 |
5 | const Nodal = require('nodal');
6 |
7 | class IndexController extends Nodal.Controller {
8 |
9 | get() {
10 |
11 | this.render(
12 | this.app.angularSPA({
13 | api_url: Nodal.my.Config.secrets.api_url
14 | })
15 | );
16 |
17 | }
18 |
19 | }
20 |
21 | return IndexController;
22 |
23 | })();
24 |
--------------------------------------------------------------------------------
/src/app/router.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 |
3 | 'use strict';
4 |
5 | const Nodal = require('nodal');
6 | const router = new Nodal.Router();
7 |
8 | /* Middleware */
9 | /* executed *before* Controller-specific middleware */
10 |
11 | const CORSMiddleware = Nodal.require('middleware/cors_middleware.js');
12 | // const ForceWWWMiddleware = Nodal.require('middleware/force_www_middleware.js');
13 | // const ForceHTTPSMiddleware = Nodal.require('middleware/force_https_middleware.js');
14 |
15 | router.middleware.use(CORSMiddleware);
16 | // router.middleware.use(ForceWWWMiddleware);
17 | // router.middleware.use(ForceHTTPSMiddleware);
18 |
19 | /* Renderware */
20 | /* executed *after* Controller-specific renderware */
21 |
22 | const GzipRenderware = Nodal.require('renderware/gzip_renderware.js')
23 |
24 | router.renderware.use(GzipRenderware);
25 |
26 | /* Routes */
27 |
28 | const IndexController = Nodal.require('app/controllers/index_controller.js');
29 | const StaticController = Nodal.require('app/controllers/static_controller.js');
30 | const Error404Controller = Nodal.require('app/controllers/error/404_controller.js');
31 |
32 | /* generator: begin imports */
33 |
34 | const V1UsersController = Nodal.require('app/controllers/v1/users_controller.js');
35 | const V1AccessTokensController = Nodal.require('app/controllers/v1/access_tokens_controller.js');
36 |
37 | /* generator: end imports */
38 |
39 | router.route('/static/*').use(StaticController);
40 |
41 | /* generator: begin routes */
42 |
43 | router.route('/v1/users/{id}').use(V1UsersController);
44 | router.route('/v1/access_tokens/{id}').use(V1AccessTokensController);
45 |
46 | /* generator: end routes */
47 |
48 | router.route('/*').use(IndexController);
49 |
50 | return router;
51 |
52 | })();
53 |
--------------------------------------------------------------------------------