├── .gitignore
├── Gruntfile.coffee
├── LICENSE
├── README.md
├── angular-parse.js
├── bower.json
├── example.js
├── example
├── coffee
│ └── app.coffee
├── css
│ └── style.css
├── index.html
├── js
│ └── app.js
├── lib
│ └── bootstrap
│ │ ├── css
│ │ ├── bootstrap-responsive.css
│ │ └── bootstrap.css
│ │ ├── img
│ │ ├── glyphicons-halflings-white.png
│ │ └── glyphicons-halflings.png
│ │ └── js
│ │ └── bootstrap.js
├── parse
│ ├── cloud
│ │ └── main.js
│ ├── config
│ │ └── global.json
│ └── src
│ │ └── main.coffee
└── partials
│ ├── detail.html
│ ├── form.html
│ ├── list.html
│ ├── register.html
│ └── sign-in.html
├── karma.conf.js
├── lib
└── angular
│ ├── angular-cookies.js
│ ├── angular-cookies.min.js
│ ├── angular-loader.js
│ ├── angular-loader.min.js
│ ├── angular-resource.js
│ ├── angular-resource.min.js
│ ├── angular-sanitize.js
│ ├── angular-sanitize.min.js
│ ├── angular.js
│ ├── angular.min.js
│ └── version.txt
├── package.json
├── src
└── angular-parse.coffee
└── test
├── support
├── angular-mocks.js
├── angular-scenario.js
└── jasmine.async.js
└── unit
├── authSpec.coffee
└── modelSpec.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 | npm-debug.log
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt) ->
2 | # Project configuration.
3 | grunt.initConfig
4 | relativePath: ''
5 |
6 | coffee:
7 | main:
8 | files: [
9 | expand: true
10 | cwd: 'src/'
11 | src: ['**/*.coffee']
12 | dest: '.'
13 | ext: '.js'
14 | ,
15 | expand: true
16 | cwd: 'example/coffee'
17 | src: ['**/*.coffee']
18 | dest: 'example/js'
19 | ext: '.js'
20 | ]
21 |
22 | karma:
23 | options:
24 | configFile: 'karma.conf.js'
25 | unit:
26 | background: true
27 | single:
28 | singleRun: true
29 |
30 | connect:
31 | main:
32 | options:
33 | port: 9001
34 | base: 'build/'
35 |
36 | watch:
37 | main:
38 | options:
39 | livereload: false
40 | files: ['src/**/*.coffee', 'test/**/*.coffee']
41 | tasks: ['coffee', 'karma:unit:run']
42 |
43 | grunt.loadNpmTasks name for name of grunt.file.readJSON('package.json').devDependencies when name[0..5] is 'grunt-'
44 |
45 | grunt.registerTask 'default', ['coffee', 'karma:unit', 'watch:main']
46 | grunt.registerTask 'test', ['karma:single']
47 | grunt.registerTask "parse-deploy", ->
48 | done = @async()
49 | grunt.utils.spawn
50 | cmd: "parse"
51 | args: ["deploy"]
52 | opts:
53 | cwd: "./example/parse"
54 | , -> done()
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Jim Hoskins
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Parse for AngularJS
2 |
3 | _This is pre-alpha/actively developed. There are no guarantees of
4 | stability, but you are welcome to play around and submit issues_
5 |
6 | angular-parse is an [AngularJS](http://angularjs.org) module for
7 | interacting with the [Parse](http://parse.com) [REST
8 | API](https://parse.com/docs/rest). It *does not* utlize the [Parse
9 | JavaScript API](https://parse.com/docs/js_guide) but instead is built
10 | from (mostly) scratch. The reason is the existing Parse JavaScript API
11 | is not ideal for AngularJS applications.
12 |
13 | # Why Angular-Parse
14 |
15 | There are a few things that are not ideal about the existing Parse
16 | JavaScript API in AngularJS. The existing API is modeled after [Backbone
17 | Models](http://backbonejs.org/#Model) and the main problem is setters
18 | are used instead of object properties. `instance.set('property', 'value')`
19 | doesn't really fit well with things like `ng-model`
20 |
21 | Instead, angular-parse is based loosely on [Spine
22 | Models](http://spinejs.com/docs/models) where properties directly
23 | defined on the object are used. To facilitate this, when defining a
24 | model, it is "configured" by supplying the class name (as defined in
25 | Parse) as well as which properties are part of that class.
26 |
27 | Angular-parse also uses promises for any methods making network calls.
28 |
29 | ## Getting started
30 |
31 | Include the JavaScript file
32 |
33 | ```html
34 |
35 |
36 | ```
37 |
38 | Make sure to add `"Parse"` as a dependency of your main module
39 |
40 | ```javascript
41 | var app = angular.module("YourApp", ["Parse"])
42 | ```
43 |
44 | Angular-parse also requires you provide the value "ParseConfig" as an
45 | object with the following format
46 |
47 | ```javascript
48 | app.config(function (ParseProvider) {
49 | ParseProvider.initialize("PARSE_APPLICATION_ID", "PARSE_REST_API_KEY");
50 | });
51 | ```
52 |
53 | ## Defining Models
54 |
55 | You can define models by extending Parse.Model. You must call configure
56 | on the class and pass it the Parse class name, and the name of any
57 | attributes of that class
58 |
59 | Using CoffeeScript:
60 | ```coffeescript
61 | app.factory 'Car', (Parse) ->
62 | class Car extends Parse.model
63 | @configure "Car", "make", "model", "year"
64 |
65 | @customClassMethod: (arg) ->
66 | # add custom class methods like this
67 |
68 | customInstanceMethod: (arg) ->
69 | # add custom instance methods like this
70 | ```
71 |
72 | Using JavaScript:
73 | ```javascript
74 | // Not implemented yet, sorry
75 | ```
76 |
77 | ## Using Models
78 |
79 | A model acts much the same as a normal JavaScript object with a
80 | constructor
81 |
82 | ### Creating a new instance
83 |
84 | You can create a new instance by using `new`. Any attributes passed in
85 | will be set on the instance. This does not save it to parse, that must
86 | be done with `.save()`. The save method returns a promise, which is
87 | fulfilled with the instance itself.
88 |
89 | ```javascript
90 | var car = new Car({
91 | make: "Scion",
92 | model: "xB",
93 | year: 2008
94 | });
95 |
96 | car.isNew() === true;
97 | car.objectId == null;
98 |
99 | car.save().then(function (_car) {
100 | _car === car;
101 | car.isNew() === false;
102 | car.objectId === "...aParseId";
103 | car.createdAt === "...aDateString";
104 | car.updatedAt === "...aDateString";
105 | }
106 | ```
107 |
108 | If the object has an objectId, it will be updated properly, and will not
109 | create a new instance. `save()` can be used either for new or existing
110 | records.
111 |
112 | ### Getting an instance By Id
113 |
114 | The `find` method on your model class takes an objectId, and returns a
115 | promise that will be fulfilled with your instance if it exists.
116 |
117 |
118 | ```javascript
119 | Car.find("someObjectId").then(function (car) {
120 | car.objectId === "someObjectId";
121 | })
122 | ```
123 |
124 | ### Destroying an instance
125 |
126 | The destroy method on an instance will destroy it set destroyed to true
127 | and set the item's objectId to null
128 |
129 | ```javascript
130 | Car.find("someObjectId").then(function (car) {
131 | car.objectId === "someObjectId";
132 |
133 | car.destroy().then(function (_car) {
134 | car === _car;
135 | car.destroyed === true;
136 | car.isNew() === true;
137 | car.objectId === null;
138 | })
139 | })
140 | ```
141 |
142 | ### Defining a custom user class
143 |
144 | A simple User class is provided to you. However, you can subclass it:
145 |
146 | ```coffeescript
147 | angular.module('Parse').factory 'ParseCustomUser', (ParseDefaultUser) ->
148 | class CustomUser extends ParseDefaultUser
149 | @configure 'users', 'username', 'password', 'property'
150 | ```
151 |
152 | In this manner, all User instances returned by the Parse methods
153 | will be of your custom class.
154 |
155 | ### Contributing
156 |
157 | Pull requests and issues are welcome.
158 |
--------------------------------------------------------------------------------
/angular-parse.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var CONFIG, module,
3 | __hasProp = {}.hasOwnProperty,
4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
5 | __slice = [].slice,
6 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
7 |
8 | module = angular.module('Parse', []);
9 |
10 | CONFIG = {};
11 |
12 | module.factory('persist', function($q, $window) {
13 | var persist, store;
14 | store = $window.localStorage;
15 | return persist = {
16 | get: function(keys) {
17 | var key, result, _i, _len;
18 | if (!angular.isArray(keys)) {
19 | keys = [keys];
20 | }
21 | result = {};
22 | for (_i = 0, _len = keys.length; _i < _len; _i++) {
23 | key = keys[_i];
24 | if (store.key(key)) {
25 | result[key] = store.getItem(key);
26 | } else {
27 | result[key] = void 0;
28 | }
29 | }
30 | return result;
31 | },
32 | set: function(obj) {
33 | var key, val;
34 | for (key in obj) {
35 | if (!__hasProp.call(obj, key)) continue;
36 | val = obj[key];
37 | store.setItem(key, val);
38 | }
39 | return true;
40 | },
41 | remove: function(keys) {
42 | var key, _i, _len;
43 | if (!angular.isArray(keys)) {
44 | keys = [keys];
45 | }
46 | for (_i = 0, _len = keys.length; _i < _len; _i++) {
47 | key = keys[_i];
48 | localStorage.removeItem(key);
49 | }
50 | return true;
51 | }
52 | };
53 | });
54 |
55 | module.factory('ParseUtils', function($http, $window) {
56 | var Parse;
57 | return Parse = {
58 | BaseUrl: "https://api.parse.com/1",
59 | _request: function(method, path, data, params) {
60 | var headers, id, klass, _ref;
61 | if (angular.isArray(path)) {
62 | klass = path[0], id = path[1];
63 | path = "" + (klass.pathBase()) + "/" + id;
64 | } else if (path.className) {
65 | path = "" + (path.pathBase());
66 | } else if (path.objectId && ((_ref = path.constructor) != null ? _ref.className : void 0)) {
67 | path = "" + (path.constructor.pathBase()) + "/" + path.objectId;
68 | }
69 | headers = {
70 | "X-Parse-Application-Id": CONFIG.applicationId,
71 | "X-Parse-REST-API-KEY": CONFIG.apiKey,
72 | "Content-Type": "application/json"
73 | };
74 | if ($window.localStorage.key('PARSE_SESSION_TOKEN')) {
75 | headers["X-Parse-Session-Token"] = $window.localStorage.getItem('PARSE_SESSION_TOKEN');
76 | }
77 | return $http({
78 | method: method,
79 | url: this.BaseUrl + path,
80 | data: data,
81 | params: params,
82 | headers: headers
83 | });
84 | },
85 | func: function(name) {
86 | return function(data) {
87 | return Parse.callFunction(name, data);
88 | };
89 | },
90 | callFunction: function(name, data) {
91 | return Parse._request("POST", "/functions/" + name, data).then(function(r) {
92 | return r.data.result;
93 | });
94 | }
95 | };
96 | });
97 |
98 | module.factory('ParseAuth', function(persist, ParseUser, ParseUtils, $q) {
99 | var auth;
100 | return auth = {
101 | sessionToken: null,
102 | currentUser: null,
103 | _login: function(user) {
104 | var info;
105 | auth.currentUser = user;
106 | auth.sessionToken = user.sessionToken;
107 | info = user.attributes();
108 | info.objectId = user.objectId;
109 | persist.set({
110 | PARSE_USER_INFO: JSON.stringify(info),
111 | PARSE_SESSION_TOKEN: user.sessionToken
112 | });
113 | return user;
114 | },
115 | resumeSession: function() {
116 | var deferred, e, results, sessionToken, user, userAttrs;
117 | results = persist.get(['PARSE_SESSION_TOKEN', 'PARSE_USER_INFO']);
118 | userAttrs = results.PARSE_USER_INFO;
119 | sessionToken = results.PARSE_SESSION_TOKEN;
120 | deferred = $q.defer();
121 | if (userAttrs && sessionToken) {
122 | try {
123 | user = new ParseUser(JSON.parse(userAttrs));
124 | auth.currentUser = user;
125 | auth.sessionToken = sessionToken;
126 | deferred.resolve(user.refresh());
127 | } catch (_error) {
128 | e = _error;
129 | deferred.reject('User attributes not parseable');
130 | }
131 | } else {
132 | deferred.reject('User attributes or Session Token not found');
133 | }
134 | return deferred.promise;
135 | },
136 | register: function(username, password) {
137 | return new ParseUser({
138 | username: username,
139 | password: password
140 | }).save().then(function(user) {
141 | return auth._login(user);
142 | });
143 | },
144 | login: function(username, password) {
145 | return ParseUtils._request("GET", "/login", null, {
146 | username: username,
147 | password: password
148 | }).then(function(response) {
149 | return auth._login(new ParseUser(response.data));
150 | });
151 | },
152 | logout: function() {
153 | persist.remove(['PARSE_SESSION_TOKEN', 'PARSE_USER_INFO']);
154 | auth.currentUser = null;
155 | return auth.sessionToken = null;
156 | }
157 | };
158 | });
159 |
160 | module.factory('ParseModel', function(ParseUtils) {
161 | var Model;
162 | return Model = (function() {
163 | Model.pathBase = function() {
164 | return "/classes/" + this.className;
165 | };
166 |
167 | Model.find = function(id, params) {
168 | var _this = this;
169 | return ParseUtils._request('GET', "/classes/" + this.className + "/" + id, null, params).then(function(response) {
170 | return new _this(response.data);
171 | });
172 | };
173 |
174 | Model.query = function(params) {
175 | var _this = this;
176 | return ParseUtils._request('GET', this, null, params).then(function(response) {
177 | var item, _i, _len, _ref, _results;
178 | _ref = response.data.results;
179 | _results = [];
180 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
181 | item = _ref[_i];
182 | _results.push(new _this(item));
183 | }
184 | return _results;
185 | });
186 | };
187 |
188 | Model.configure = function() {
189 | var attributes, name;
190 | name = arguments[0], attributes = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
191 | this.className = name;
192 | return this.attributes = attributes;
193 | };
194 |
195 | function Model(data) {
196 | this.isDirty = __bind(this.isDirty, this);
197 | this._saveCache = __bind(this._saveCache, this);
198 | this.encodeParse = __bind(this.encodeParse, this);
199 | this.attributes = __bind(this.attributes, this);
200 | this.destroy = __bind(this.destroy, this);
201 | this.update = __bind(this.update, this);
202 | this.create = __bind(this.create, this);
203 | this.refresh = __bind(this.refresh, this);
204 | this.save = __bind(this.save, this);
205 | this.isNew = __bind(this.isNew, this);
206 | var key, value;
207 | for (key in data) {
208 | value = data[key];
209 | this[key] = value;
210 | }
211 | this._saveCache();
212 | }
213 |
214 | Model.prototype.isNew = function() {
215 | return !this.objectId;
216 | };
217 |
218 | Model.prototype.save = function() {
219 | if (this.isNew()) {
220 | return this.create();
221 | } else {
222 | return this.update();
223 | }
224 | };
225 |
226 | Model.prototype.refresh = function() {
227 | var _this = this;
228 | return ParseUtils._request('GET', this).then(function(response) {
229 | var key, value, _ref;
230 | _ref = response.data;
231 | for (key in _ref) {
232 | if (!__hasProp.call(_ref, key)) continue;
233 | value = _ref[key];
234 | _this[key] = value;
235 | }
236 | return _this;
237 | });
238 | };
239 |
240 | Model.prototype.create = function() {
241 | var _this = this;
242 | return ParseUtils._request('POST', this.constructor, this.encodeParse()).then(function(response) {
243 | var token;
244 | _this.objectId = response.data.objectId;
245 | _this.createdAt = response.data.createdAt;
246 | if (token = response.data.sessionToken) {
247 | _this.sessionToken = token;
248 | }
249 | _this._saveCache();
250 | return _this;
251 | });
252 | };
253 |
254 | Model.prototype.update = function() {
255 | var _this = this;
256 | return ParseUtils._request('PUT', this, this.encodeParse()).then(function(response) {
257 | _this.updatedAt = response.data.updatedAt;
258 | _this._saveCache();
259 | return _this;
260 | });
261 | };
262 |
263 | Model.prototype.destroy = function() {
264 | var _this = this;
265 | return ParseUtils._request('DELETE', this).then(function(response) {
266 | _this.objectId = null;
267 | return _this;
268 | });
269 | };
270 |
271 | Model.prototype.attributes = function() {
272 | var key, result, _i, _len, _ref;
273 | result = {};
274 | _ref = this.constructor.attributes;
275 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
276 | key = _ref[_i];
277 | result[key] = this[key];
278 | }
279 | return result;
280 | };
281 |
282 | Model.prototype.encodeParse = function() {
283 | var key, obj, result, _i, _len, _ref, _ref1;
284 | result = {};
285 | _ref = this.constructor.attributes;
286 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
287 | key = _ref[_i];
288 | if (key in this) {
289 | obj = this[key];
290 | if ((obj != null) && obj.objectId && ((_ref1 = obj.constructor) != null ? _ref1.className : void 0)) {
291 | obj = {
292 | __type: "Pointer",
293 | className: obj.constructor.className,
294 | objectId: obj.objectId
295 | };
296 | }
297 | result[key] = obj;
298 | }
299 | }
300 | return result;
301 | };
302 |
303 | Model.prototype._saveCache = function() {
304 | return this._cache = angular.copy(this.encodeParse());
305 | };
306 |
307 | Model.prototype.isDirty = function() {
308 | return !angular.equals(this._cache, this.encodeParse());
309 | };
310 |
311 | return Model;
312 |
313 | })();
314 | });
315 |
316 | module.factory('ParseDefaultUser', function(ParseModel) {
317 | var User, _ref;
318 | return User = (function(_super) {
319 | __extends(User, _super);
320 |
321 | function User() {
322 | _ref = User.__super__.constructor.apply(this, arguments);
323 | return _ref;
324 | }
325 |
326 | User.configure('users', 'username', 'password');
327 |
328 | User.pathBase = function() {
329 | return "/users";
330 | };
331 |
332 | User.prototype.save = function() {
333 | var _this = this;
334 | return User.__super__.save.call(this).then(function(user) {
335 | delete user.password;
336 | return user;
337 | });
338 | };
339 |
340 | return User;
341 |
342 | })(ParseModel);
343 | });
344 |
345 | module.factory('ParseUser', function(ParseDefaultUser, ParseCustomUser) {
346 | if ((ParseCustomUser != null) && (new ParseCustomUser instanceof ParseDefaultUser)) {
347 | return ParseCustomUser;
348 | } else {
349 | return ParseDefaultUser;
350 | }
351 | });
352 |
353 | module.provider('Parse', function() {
354 | return {
355 | initialize: function(applicationId, apiKey) {
356 | CONFIG.apiKey = apiKey;
357 | return CONFIG.applicationId = applicationId;
358 | },
359 | $get: function(ParseModel, ParseUser, ParseAuth, ParseUtils) {
360 | return {
361 | BaseUrl: ParseUtils.BaseUrl,
362 | Model: ParseModel,
363 | User: ParseUser,
364 | auth: ParseAuth
365 | };
366 | }
367 | };
368 | });
369 |
370 | angular.module('Parse').factory('ParseCustomUser', function(ParseDefaultUser) {
371 | return ParseDefaultUser;
372 | });
373 |
374 | }).call(this);
375 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-parse",
3 | "version": "0.3.0",
4 | "main": [
5 | "angular-parse.js"
6 | ],
7 | "dependencies": {
8 | },
9 | "install": {
10 | "path": "lib"
11 | }
12 | }
--------------------------------------------------------------------------------
/example.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('parseExample', ['parse']);
2 |
--------------------------------------------------------------------------------
/example/coffee/app.coffee:
--------------------------------------------------------------------------------
1 | app = angular.module 'Forum', ['Parse']
2 |
3 | app.run (Parse) ->
4 | Parse.auth.resumeSession()
5 |
6 | app.config (ParseProvider, $routeProvider) ->
7 | ParseProvider.initialize(
8 | "x58FvFYxi4SDIRdJck4hFVv1huo8F409UnfERfUU",
9 | "ixgyh7GKI1eLSU7PYrDuuOEh31CAlYmCXS7tuJ5p"
10 | )
11 | findPostById = (Post, $route) ->
12 | if id = $route.current.params.id
13 | Post.find(id, include: "author")
14 | else
15 | new Post
16 |
17 |
18 | $routeProvider
19 | .when("/", controller: "PostList", templateUrl: "partials/list.html")
20 | .when("/register",
21 | controller: "RegisterCtrl"
22 | templateUrl: "partials/register.html"
23 | )
24 | .when("/sign-in",
25 | controller: "SignInCtrl"
26 | templateUrl: "partials/sign-in.html"
27 | )
28 | .when("/new-post",
29 | controller: "PostForm"
30 | templateUrl: "partials/form.html"
31 | resolve:
32 | $post: findPostById
33 | )
34 | .when("/posts/:id",
35 | controller: "PostDetail"
36 | templateUrl: "partials/detail.html"
37 | resolve:
38 | $post: findPostById
39 | )
40 | .when("/edit-post/:id",
41 | controller: "PostForm"
42 | templateUrl: "partials/form.html"
43 | resolve:
44 | $post: findPostById
45 | )
46 | .otherwise(redirectTo: "/")
47 |
48 |
49 | app.factory 'Post', (Parse) ->
50 | class Post extends Parse.Model
51 | @configure 'Post', 'title', 'body', 'author', 'tags', 'commentCount'
52 |
53 | app.factory 'Comment', (Parse) ->
54 | class Comment extends Parse.Model
55 | @configure 'Comment', 'author', 'post', 'body'
56 |
57 |
58 | app.controller 'PostList', ($scope, Post) ->
59 | $scope.load = ->
60 | Post.query({include: 'author'}).then (posts) ->
61 | $scope.posts = posts
62 |
63 | $scope.destroy = (post) ->
64 | post.destroy().then -> $scope.load()
65 |
66 | $scope.load()
67 |
68 | app.controller 'PostDetail', ($scope, $routeParams, $post, Comment) ->
69 | $scope.post = $post
70 | $scope.comments = []
71 |
72 | loadComments = ->
73 | Comment.query(
74 | where:
75 | post:
76 | __type: 'Pointer'
77 | className: 'Post'
78 | objectId: $post.objectId
79 | include: 'author'
80 | ).then (comments) ->
81 | $scope.comments = comments
82 | console.log comments
83 | , ->
84 | console.log arguments
85 |
86 | $scope.$on 'new-comment', loadComments
87 | loadComments()
88 |
89 | app.controller 'PostForm', ($scope, $location, Post) ->
90 | $scope.post = new Post
91 | $scope.hideForm = true
92 |
93 | $scope.save = ->
94 | $scope.post?.save().then (post) ->
95 | console.log post
96 | $location.path("/posts/#{post.objectId}")
97 | , (res) ->
98 | console.log res
99 |
100 | app.controller 'CommentForm', ($scope, Comment) ->
101 | $scope.comment = new Comment(post: $scope.post)
102 | console.log $scope.comment
103 |
104 | $scope.save = ->
105 | $scope.comment.save().then ->
106 | console.log 'S', arguments
107 | $scope.$emit 'new-comment'
108 | $scope.comment = new Comment(post: $scope.post)
109 | $scope.numRows = 1
110 | , ->
111 | console.log 'F', arguments
112 |
113 |
114 | app.controller 'AuthCtrl', ($scope, Parse) ->
115 | $scope.auth = Parse.auth
116 | $scope.signout = ->
117 | Parse.auth.logout()
118 |
119 | app.controller 'RegisterCtrl', ($location, $window, $scope, Parse) ->
120 | $scope.auth = Parse.auth
121 | $scope.user = {}
122 | $scope.errorMessage = null
123 |
124 | $scope.register = (user) ->
125 | if user.password isnt user.passwordConfirm
126 | return $scope.errorMessage = "Passwords must match"
127 |
128 | unless user.username and user.password
129 | return $scope.errorMessage = 'Please supply a username and password'
130 |
131 | Parse.auth.register(user.username, user.password).then ->
132 | $location.path("/")
133 | , (err) ->
134 | $scope.errorMessage = err.data.error
135 |
136 |
137 | app.controller 'SignInCtrl', ($location, $window, $scope, Parse) ->
138 | $scope.auth = Parse.auth
139 | $scope.user = {}
140 | $scope.errorMessage = null
141 |
142 | $scope.signin = (user) ->
143 | unless user.username and user.password
144 | return $scope.errorMessage = 'Please supply a username and password'
145 |
146 | Parse.auth.login(user.username, user.password).then ->
147 | console.log 'in', arguments
148 | $location.path("/")
149 | , (err) ->
150 | console.log 'out', arguments
151 | $scope.errorMessage = err.data.error
152 |
153 |
--------------------------------------------------------------------------------
/example/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #E3E3E3;
3 | }
4 |
5 | body > .navbar {
6 | margin-bottom: 50px;
7 | }
8 |
9 | #main {
10 | background: #FFF;
11 | padding: 10px;
12 | margin: 10px -10px;
13 | box-shadow: 0 0 1px #000;
14 | }
15 |
16 | .header {
17 | border-bottom: solid 1px #CCC;
18 | padding-bottom: 10px;
19 | }
20 |
21 | .new-form {
22 | background: #E0E0E7;
23 | padding: 10px;
24 | margin: 10px -10px;
25 | padding-bottom: -10px;
26 | }
27 |
28 | .post .title {
29 | font-size: 2em;
30 | }
31 |
32 | .comment-count {
33 | font-size: 2em;
34 | font-weight: bold;
35 | text-align: center;
36 | }
37 |
38 | form {
39 | margin: 0;
40 | }
41 |
42 | form input.input-xxlarge,
43 | form textarea.input-xxlarge {
44 | width: 680px;
45 | font-size: 24px;
46 | line-height: 24px;
47 | height: auto;
48 | }
49 |
50 |
51 | .post {
52 | margin-bottom: 20px;
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Parse Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
27 |
28 | Signed in as {{auth.currentUser.username}}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
63 |
64 |
Signed In
65 | {{auth.currentUser.username}}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/example/js/app.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var app,
3 | __hasProp = {}.hasOwnProperty,
4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
5 |
6 | app = angular.module('Forum', ['Parse']);
7 |
8 | app.run(function(Parse) {
9 | return Parse.auth.resumeSession();
10 | });
11 |
12 | app.config(function(ParseProvider, $routeProvider) {
13 | var findPostById;
14 | ParseProvider.initialize("x58FvFYxi4SDIRdJck4hFVv1huo8F409UnfERfUU", "ixgyh7GKI1eLSU7PYrDuuOEh31CAlYmCXS7tuJ5p");
15 | findPostById = function(Post, $route) {
16 | var id;
17 | if (id = $route.current.params.id) {
18 | return Post.find(id, {
19 | include: "author"
20 | });
21 | } else {
22 | return new Post;
23 | }
24 | };
25 | return $routeProvider.when("/", {
26 | controller: "PostList",
27 | templateUrl: "partials/list.html"
28 | }).when("/register", {
29 | controller: "RegisterCtrl",
30 | templateUrl: "partials/register.html"
31 | }).when("/sign-in", {
32 | controller: "SignInCtrl",
33 | templateUrl: "partials/sign-in.html"
34 | }).when("/new-post", {
35 | controller: "PostForm",
36 | templateUrl: "partials/form.html",
37 | resolve: {
38 | $post: findPostById
39 | }
40 | }).when("/posts/:id", {
41 | controller: "PostDetail",
42 | templateUrl: "partials/detail.html",
43 | resolve: {
44 | $post: findPostById
45 | }
46 | }).when("/edit-post/:id", {
47 | controller: "PostForm",
48 | templateUrl: "partials/form.html",
49 | resolve: {
50 | $post: findPostById
51 | }
52 | }).otherwise({
53 | redirectTo: "/"
54 | });
55 | });
56 |
57 | app.factory('Post', function(Parse) {
58 | var Post, _ref;
59 | return Post = (function(_super) {
60 | __extends(Post, _super);
61 |
62 | function Post() {
63 | _ref = Post.__super__.constructor.apply(this, arguments);
64 | return _ref;
65 | }
66 |
67 | Post.configure('Post', 'title', 'body', 'author', 'tags', 'commentCount');
68 |
69 | return Post;
70 |
71 | })(Parse.Model);
72 | });
73 |
74 | app.factory('Comment', function(Parse) {
75 | var Comment, _ref;
76 | return Comment = (function(_super) {
77 | __extends(Comment, _super);
78 |
79 | function Comment() {
80 | _ref = Comment.__super__.constructor.apply(this, arguments);
81 | return _ref;
82 | }
83 |
84 | Comment.configure('Comment', 'author', 'post', 'body');
85 |
86 | return Comment;
87 |
88 | })(Parse.Model);
89 | });
90 |
91 | app.controller('PostList', function($scope, Post) {
92 | $scope.load = function() {
93 | return Post.query({
94 | include: 'author'
95 | }).then(function(posts) {
96 | return $scope.posts = posts;
97 | });
98 | };
99 | $scope.destroy = function(post) {
100 | return post.destroy().then(function() {
101 | return $scope.load();
102 | });
103 | };
104 | return $scope.load();
105 | });
106 |
107 | app.controller('PostDetail', function($scope, $routeParams, $post, Comment) {
108 | var loadComments;
109 | $scope.post = $post;
110 | $scope.comments = [];
111 | loadComments = function() {
112 | return Comment.query({
113 | where: {
114 | post: {
115 | __type: 'Pointer',
116 | className: 'Post',
117 | objectId: $post.objectId
118 | }
119 | },
120 | include: 'author'
121 | }).then(function(comments) {
122 | $scope.comments = comments;
123 | return console.log(comments);
124 | }, function() {
125 | return console.log(arguments);
126 | });
127 | };
128 | $scope.$on('new-comment', loadComments);
129 | return loadComments();
130 | });
131 |
132 | app.controller('PostForm', function($scope, $location, Post) {
133 | $scope.post = new Post;
134 | $scope.hideForm = true;
135 | return $scope.save = function() {
136 | var _ref;
137 | return (_ref = $scope.post) != null ? _ref.save().then(function(post) {
138 | console.log(post);
139 | return $location.path("/posts/" + post.objectId);
140 | }, function(res) {
141 | return console.log(res);
142 | }) : void 0;
143 | };
144 | });
145 |
146 | app.controller('CommentForm', function($scope, Comment) {
147 | $scope.comment = new Comment({
148 | post: $scope.post
149 | });
150 | console.log($scope.comment);
151 | return $scope.save = function() {
152 | return $scope.comment.save().then(function() {
153 | console.log('S', arguments);
154 | $scope.$emit('new-comment');
155 | $scope.comment = new Comment({
156 | post: $scope.post
157 | });
158 | return $scope.numRows = 1;
159 | }, function() {
160 | return console.log('F', arguments);
161 | });
162 | };
163 | });
164 |
165 | app.controller('AuthCtrl', function($scope, Parse) {
166 | $scope.auth = Parse.auth;
167 | return $scope.signout = function() {
168 | return Parse.auth.logout();
169 | };
170 | });
171 |
172 | app.controller('RegisterCtrl', function($location, $window, $scope, Parse) {
173 | $scope.auth = Parse.auth;
174 | $scope.user = {};
175 | $scope.errorMessage = null;
176 | return $scope.register = function(user) {
177 | if (user.password !== user.passwordConfirm) {
178 | return $scope.errorMessage = "Passwords must match";
179 | }
180 | if (!(user.username && user.password)) {
181 | return $scope.errorMessage = 'Please supply a username and password';
182 | }
183 | return Parse.auth.register(user.username, user.password).then(function() {
184 | return $location.path("/");
185 | }, function(err) {
186 | return $scope.errorMessage = err.data.error;
187 | });
188 | };
189 | });
190 |
191 | app.controller('SignInCtrl', function($location, $window, $scope, Parse) {
192 | $scope.auth = Parse.auth;
193 | $scope.user = {};
194 | $scope.errorMessage = null;
195 | return $scope.signin = function(user) {
196 | if (!(user.username && user.password)) {
197 | return $scope.errorMessage = 'Please supply a username and password';
198 | }
199 | return Parse.auth.login(user.username, user.password).then(function() {
200 | console.log('in', arguments);
201 | return $location.path("/");
202 | }, function(err) {
203 | console.log('out', arguments);
204 | return $scope.errorMessage = err.data.error;
205 | });
206 | };
207 | });
208 |
209 | }).call(this);
210 |
--------------------------------------------------------------------------------
/example/lib/bootstrap/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.2.2
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | @-ms-viewport {
12 | width: device-width;
13 | }
14 |
15 | .clearfix {
16 | *zoom: 1;
17 | }
18 |
19 | .clearfix:before,
20 | .clearfix:after {
21 | display: table;
22 | line-height: 0;
23 | content: "";
24 | }
25 |
26 | .clearfix:after {
27 | clear: both;
28 | }
29 |
30 | .hide-text {
31 | font: 0/0 a;
32 | color: transparent;
33 | text-shadow: none;
34 | background-color: transparent;
35 | border: 0;
36 | }
37 |
38 | .input-block-level {
39 | display: block;
40 | width: 100%;
41 | min-height: 30px;
42 | -webkit-box-sizing: border-box;
43 | -moz-box-sizing: border-box;
44 | box-sizing: border-box;
45 | }
46 |
47 | .hidden {
48 | display: none;
49 | visibility: hidden;
50 | }
51 |
52 | .visible-phone {
53 | display: none !important;
54 | }
55 |
56 | .visible-tablet {
57 | display: none !important;
58 | }
59 |
60 | .hidden-desktop {
61 | display: none !important;
62 | }
63 |
64 | .visible-desktop {
65 | display: inherit !important;
66 | }
67 |
68 | @media (min-width: 768px) and (max-width: 979px) {
69 | .hidden-desktop {
70 | display: inherit !important;
71 | }
72 | .visible-desktop {
73 | display: none !important ;
74 | }
75 | .visible-tablet {
76 | display: inherit !important;
77 | }
78 | .hidden-tablet {
79 | display: none !important;
80 | }
81 | }
82 |
83 | @media (max-width: 767px) {
84 | .hidden-desktop {
85 | display: inherit !important;
86 | }
87 | .visible-desktop {
88 | display: none !important;
89 | }
90 | .visible-phone {
91 | display: inherit !important;
92 | }
93 | .hidden-phone {
94 | display: none !important;
95 | }
96 | }
97 |
98 | @media (min-width: 1200px) {
99 | .row {
100 | margin-left: -30px;
101 | *zoom: 1;
102 | }
103 | .row:before,
104 | .row:after {
105 | display: table;
106 | line-height: 0;
107 | content: "";
108 | }
109 | .row:after {
110 | clear: both;
111 | }
112 | [class*="span"] {
113 | float: left;
114 | min-height: 1px;
115 | margin-left: 30px;
116 | }
117 | .container,
118 | .navbar-static-top .container,
119 | .navbar-fixed-top .container,
120 | .navbar-fixed-bottom .container {
121 | width: 1170px;
122 | }
123 | .span12 {
124 | width: 1170px;
125 | }
126 | .span11 {
127 | width: 1070px;
128 | }
129 | .span10 {
130 | width: 970px;
131 | }
132 | .span9 {
133 | width: 870px;
134 | }
135 | .span8 {
136 | width: 770px;
137 | }
138 | .span7 {
139 | width: 670px;
140 | }
141 | .span6 {
142 | width: 570px;
143 | }
144 | .span5 {
145 | width: 470px;
146 | }
147 | .span4 {
148 | width: 370px;
149 | }
150 | .span3 {
151 | width: 270px;
152 | }
153 | .span2 {
154 | width: 170px;
155 | }
156 | .span1 {
157 | width: 70px;
158 | }
159 | .offset12 {
160 | margin-left: 1230px;
161 | }
162 | .offset11 {
163 | margin-left: 1130px;
164 | }
165 | .offset10 {
166 | margin-left: 1030px;
167 | }
168 | .offset9 {
169 | margin-left: 930px;
170 | }
171 | .offset8 {
172 | margin-left: 830px;
173 | }
174 | .offset7 {
175 | margin-left: 730px;
176 | }
177 | .offset6 {
178 | margin-left: 630px;
179 | }
180 | .offset5 {
181 | margin-left: 530px;
182 | }
183 | .offset4 {
184 | margin-left: 430px;
185 | }
186 | .offset3 {
187 | margin-left: 330px;
188 | }
189 | .offset2 {
190 | margin-left: 230px;
191 | }
192 | .offset1 {
193 | margin-left: 130px;
194 | }
195 | .row-fluid {
196 | width: 100%;
197 | *zoom: 1;
198 | }
199 | .row-fluid:before,
200 | .row-fluid:after {
201 | display: table;
202 | line-height: 0;
203 | content: "";
204 | }
205 | .row-fluid:after {
206 | clear: both;
207 | }
208 | .row-fluid [class*="span"] {
209 | display: block;
210 | float: left;
211 | width: 100%;
212 | min-height: 30px;
213 | margin-left: 2.564102564102564%;
214 | *margin-left: 2.5109110747408616%;
215 | -webkit-box-sizing: border-box;
216 | -moz-box-sizing: border-box;
217 | box-sizing: border-box;
218 | }
219 | .row-fluid [class*="span"]:first-child {
220 | margin-left: 0;
221 | }
222 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
223 | margin-left: 2.564102564102564%;
224 | }
225 | .row-fluid .span12 {
226 | width: 100%;
227 | *width: 99.94680851063829%;
228 | }
229 | .row-fluid .span11 {
230 | width: 91.45299145299145%;
231 | *width: 91.39979996362975%;
232 | }
233 | .row-fluid .span10 {
234 | width: 82.90598290598291%;
235 | *width: 82.8527914166212%;
236 | }
237 | .row-fluid .span9 {
238 | width: 74.35897435897436%;
239 | *width: 74.30578286961266%;
240 | }
241 | .row-fluid .span8 {
242 | width: 65.81196581196582%;
243 | *width: 65.75877432260411%;
244 | }
245 | .row-fluid .span7 {
246 | width: 57.26495726495726%;
247 | *width: 57.21176577559556%;
248 | }
249 | .row-fluid .span6 {
250 | width: 48.717948717948715%;
251 | *width: 48.664757228587014%;
252 | }
253 | .row-fluid .span5 {
254 | width: 40.17094017094017%;
255 | *width: 40.11774868157847%;
256 | }
257 | .row-fluid .span4 {
258 | width: 31.623931623931625%;
259 | *width: 31.570740134569924%;
260 | }
261 | .row-fluid .span3 {
262 | width: 23.076923076923077%;
263 | *width: 23.023731587561375%;
264 | }
265 | .row-fluid .span2 {
266 | width: 14.52991452991453%;
267 | *width: 14.476723040552828%;
268 | }
269 | .row-fluid .span1 {
270 | width: 5.982905982905983%;
271 | *width: 5.929714493544281%;
272 | }
273 | .row-fluid .offset12 {
274 | margin-left: 105.12820512820512%;
275 | *margin-left: 105.02182214948171%;
276 | }
277 | .row-fluid .offset12:first-child {
278 | margin-left: 102.56410256410257%;
279 | *margin-left: 102.45771958537915%;
280 | }
281 | .row-fluid .offset11 {
282 | margin-left: 96.58119658119658%;
283 | *margin-left: 96.47481360247316%;
284 | }
285 | .row-fluid .offset11:first-child {
286 | margin-left: 94.01709401709402%;
287 | *margin-left: 93.91071103837061%;
288 | }
289 | .row-fluid .offset10 {
290 | margin-left: 88.03418803418803%;
291 | *margin-left: 87.92780505546462%;
292 | }
293 | .row-fluid .offset10:first-child {
294 | margin-left: 85.47008547008548%;
295 | *margin-left: 85.36370249136206%;
296 | }
297 | .row-fluid .offset9 {
298 | margin-left: 79.48717948717949%;
299 | *margin-left: 79.38079650845607%;
300 | }
301 | .row-fluid .offset9:first-child {
302 | margin-left: 76.92307692307693%;
303 | *margin-left: 76.81669394435352%;
304 | }
305 | .row-fluid .offset8 {
306 | margin-left: 70.94017094017094%;
307 | *margin-left: 70.83378796144753%;
308 | }
309 | .row-fluid .offset8:first-child {
310 | margin-left: 68.37606837606839%;
311 | *margin-left: 68.26968539734497%;
312 | }
313 | .row-fluid .offset7 {
314 | margin-left: 62.393162393162385%;
315 | *margin-left: 62.28677941443899%;
316 | }
317 | .row-fluid .offset7:first-child {
318 | margin-left: 59.82905982905982%;
319 | *margin-left: 59.72267685033642%;
320 | }
321 | .row-fluid .offset6 {
322 | margin-left: 53.84615384615384%;
323 | *margin-left: 53.739770867430444%;
324 | }
325 | .row-fluid .offset6:first-child {
326 | margin-left: 51.28205128205128%;
327 | *margin-left: 51.175668303327875%;
328 | }
329 | .row-fluid .offset5 {
330 | margin-left: 45.299145299145295%;
331 | *margin-left: 45.1927623204219%;
332 | }
333 | .row-fluid .offset5:first-child {
334 | margin-left: 42.73504273504273%;
335 | *margin-left: 42.62865975631933%;
336 | }
337 | .row-fluid .offset4 {
338 | margin-left: 36.75213675213675%;
339 | *margin-left: 36.645753773413354%;
340 | }
341 | .row-fluid .offset4:first-child {
342 | margin-left: 34.18803418803419%;
343 | *margin-left: 34.081651209310785%;
344 | }
345 | .row-fluid .offset3 {
346 | margin-left: 28.205128205128204%;
347 | *margin-left: 28.0987452264048%;
348 | }
349 | .row-fluid .offset3:first-child {
350 | margin-left: 25.641025641025642%;
351 | *margin-left: 25.53464266230224%;
352 | }
353 | .row-fluid .offset2 {
354 | margin-left: 19.65811965811966%;
355 | *margin-left: 19.551736679396257%;
356 | }
357 | .row-fluid .offset2:first-child {
358 | margin-left: 17.094017094017094%;
359 | *margin-left: 16.98763411529369%;
360 | }
361 | .row-fluid .offset1 {
362 | margin-left: 11.11111111111111%;
363 | *margin-left: 11.004728132387708%;
364 | }
365 | .row-fluid .offset1:first-child {
366 | margin-left: 8.547008547008547%;
367 | *margin-left: 8.440625568285142%;
368 | }
369 | input,
370 | textarea,
371 | .uneditable-input {
372 | margin-left: 0;
373 | }
374 | .controls-row [class*="span"] + [class*="span"] {
375 | margin-left: 30px;
376 | }
377 | input.span12,
378 | textarea.span12,
379 | .uneditable-input.span12 {
380 | width: 1156px;
381 | }
382 | input.span11,
383 | textarea.span11,
384 | .uneditable-input.span11 {
385 | width: 1056px;
386 | }
387 | input.span10,
388 | textarea.span10,
389 | .uneditable-input.span10 {
390 | width: 956px;
391 | }
392 | input.span9,
393 | textarea.span9,
394 | .uneditable-input.span9 {
395 | width: 856px;
396 | }
397 | input.span8,
398 | textarea.span8,
399 | .uneditable-input.span8 {
400 | width: 756px;
401 | }
402 | input.span7,
403 | textarea.span7,
404 | .uneditable-input.span7 {
405 | width: 656px;
406 | }
407 | input.span6,
408 | textarea.span6,
409 | .uneditable-input.span6 {
410 | width: 556px;
411 | }
412 | input.span5,
413 | textarea.span5,
414 | .uneditable-input.span5 {
415 | width: 456px;
416 | }
417 | input.span4,
418 | textarea.span4,
419 | .uneditable-input.span4 {
420 | width: 356px;
421 | }
422 | input.span3,
423 | textarea.span3,
424 | .uneditable-input.span3 {
425 | width: 256px;
426 | }
427 | input.span2,
428 | textarea.span2,
429 | .uneditable-input.span2 {
430 | width: 156px;
431 | }
432 | input.span1,
433 | textarea.span1,
434 | .uneditable-input.span1 {
435 | width: 56px;
436 | }
437 | .thumbnails {
438 | margin-left: -30px;
439 | }
440 | .thumbnails > li {
441 | margin-left: 30px;
442 | }
443 | .row-fluid .thumbnails {
444 | margin-left: 0;
445 | }
446 | }
447 |
448 | @media (min-width: 768px) and (max-width: 979px) {
449 | .row {
450 | margin-left: -20px;
451 | *zoom: 1;
452 | }
453 | .row:before,
454 | .row:after {
455 | display: table;
456 | line-height: 0;
457 | content: "";
458 | }
459 | .row:after {
460 | clear: both;
461 | }
462 | [class*="span"] {
463 | float: left;
464 | min-height: 1px;
465 | margin-left: 20px;
466 | }
467 | .container,
468 | .navbar-static-top .container,
469 | .navbar-fixed-top .container,
470 | .navbar-fixed-bottom .container {
471 | width: 724px;
472 | }
473 | .span12 {
474 | width: 724px;
475 | }
476 | .span11 {
477 | width: 662px;
478 | }
479 | .span10 {
480 | width: 600px;
481 | }
482 | .span9 {
483 | width: 538px;
484 | }
485 | .span8 {
486 | width: 476px;
487 | }
488 | .span7 {
489 | width: 414px;
490 | }
491 | .span6 {
492 | width: 352px;
493 | }
494 | .span5 {
495 | width: 290px;
496 | }
497 | .span4 {
498 | width: 228px;
499 | }
500 | .span3 {
501 | width: 166px;
502 | }
503 | .span2 {
504 | width: 104px;
505 | }
506 | .span1 {
507 | width: 42px;
508 | }
509 | .offset12 {
510 | margin-left: 764px;
511 | }
512 | .offset11 {
513 | margin-left: 702px;
514 | }
515 | .offset10 {
516 | margin-left: 640px;
517 | }
518 | .offset9 {
519 | margin-left: 578px;
520 | }
521 | .offset8 {
522 | margin-left: 516px;
523 | }
524 | .offset7 {
525 | margin-left: 454px;
526 | }
527 | .offset6 {
528 | margin-left: 392px;
529 | }
530 | .offset5 {
531 | margin-left: 330px;
532 | }
533 | .offset4 {
534 | margin-left: 268px;
535 | }
536 | .offset3 {
537 | margin-left: 206px;
538 | }
539 | .offset2 {
540 | margin-left: 144px;
541 | }
542 | .offset1 {
543 | margin-left: 82px;
544 | }
545 | .row-fluid {
546 | width: 100%;
547 | *zoom: 1;
548 | }
549 | .row-fluid:before,
550 | .row-fluid:after {
551 | display: table;
552 | line-height: 0;
553 | content: "";
554 | }
555 | .row-fluid:after {
556 | clear: both;
557 | }
558 | .row-fluid [class*="span"] {
559 | display: block;
560 | float: left;
561 | width: 100%;
562 | min-height: 30px;
563 | margin-left: 2.7624309392265194%;
564 | *margin-left: 2.709239449864817%;
565 | -webkit-box-sizing: border-box;
566 | -moz-box-sizing: border-box;
567 | box-sizing: border-box;
568 | }
569 | .row-fluid [class*="span"]:first-child {
570 | margin-left: 0;
571 | }
572 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
573 | margin-left: 2.7624309392265194%;
574 | }
575 | .row-fluid .span12 {
576 | width: 100%;
577 | *width: 99.94680851063829%;
578 | }
579 | .row-fluid .span11 {
580 | width: 91.43646408839778%;
581 | *width: 91.38327259903608%;
582 | }
583 | .row-fluid .span10 {
584 | width: 82.87292817679558%;
585 | *width: 82.81973668743387%;
586 | }
587 | .row-fluid .span9 {
588 | width: 74.30939226519337%;
589 | *width: 74.25620077583166%;
590 | }
591 | .row-fluid .span8 {
592 | width: 65.74585635359117%;
593 | *width: 65.69266486422946%;
594 | }
595 | .row-fluid .span7 {
596 | width: 57.18232044198895%;
597 | *width: 57.12912895262725%;
598 | }
599 | .row-fluid .span6 {
600 | width: 48.61878453038674%;
601 | *width: 48.56559304102504%;
602 | }
603 | .row-fluid .span5 {
604 | width: 40.05524861878453%;
605 | *width: 40.00205712942283%;
606 | }
607 | .row-fluid .span4 {
608 | width: 31.491712707182323%;
609 | *width: 31.43852121782062%;
610 | }
611 | .row-fluid .span3 {
612 | width: 22.92817679558011%;
613 | *width: 22.87498530621841%;
614 | }
615 | .row-fluid .span2 {
616 | width: 14.3646408839779%;
617 | *width: 14.311449394616199%;
618 | }
619 | .row-fluid .span1 {
620 | width: 5.801104972375691%;
621 | *width: 5.747913483013988%;
622 | }
623 | .row-fluid .offset12 {
624 | margin-left: 105.52486187845304%;
625 | *margin-left: 105.41847889972962%;
626 | }
627 | .row-fluid .offset12:first-child {
628 | margin-left: 102.76243093922652%;
629 | *margin-left: 102.6560479605031%;
630 | }
631 | .row-fluid .offset11 {
632 | margin-left: 96.96132596685082%;
633 | *margin-left: 96.8549429881274%;
634 | }
635 | .row-fluid .offset11:first-child {
636 | margin-left: 94.1988950276243%;
637 | *margin-left: 94.09251204890089%;
638 | }
639 | .row-fluid .offset10 {
640 | margin-left: 88.39779005524862%;
641 | *margin-left: 88.2914070765252%;
642 | }
643 | .row-fluid .offset10:first-child {
644 | margin-left: 85.6353591160221%;
645 | *margin-left: 85.52897613729868%;
646 | }
647 | .row-fluid .offset9 {
648 | margin-left: 79.8342541436464%;
649 | *margin-left: 79.72787116492299%;
650 | }
651 | .row-fluid .offset9:first-child {
652 | margin-left: 77.07182320441989%;
653 | *margin-left: 76.96544022569647%;
654 | }
655 | .row-fluid .offset8 {
656 | margin-left: 71.2707182320442%;
657 | *margin-left: 71.16433525332079%;
658 | }
659 | .row-fluid .offset8:first-child {
660 | margin-left: 68.50828729281768%;
661 | *margin-left: 68.40190431409427%;
662 | }
663 | .row-fluid .offset7 {
664 | margin-left: 62.70718232044199%;
665 | *margin-left: 62.600799341718584%;
666 | }
667 | .row-fluid .offset7:first-child {
668 | margin-left: 59.94475138121547%;
669 | *margin-left: 59.838368402492065%;
670 | }
671 | .row-fluid .offset6 {
672 | margin-left: 54.14364640883978%;
673 | *margin-left: 54.037263430116376%;
674 | }
675 | .row-fluid .offset6:first-child {
676 | margin-left: 51.38121546961326%;
677 | *margin-left: 51.27483249088986%;
678 | }
679 | .row-fluid .offset5 {
680 | margin-left: 45.58011049723757%;
681 | *margin-left: 45.47372751851417%;
682 | }
683 | .row-fluid .offset5:first-child {
684 | margin-left: 42.81767955801105%;
685 | *margin-left: 42.71129657928765%;
686 | }
687 | .row-fluid .offset4 {
688 | margin-left: 37.01657458563536%;
689 | *margin-left: 36.91019160691196%;
690 | }
691 | .row-fluid .offset4:first-child {
692 | margin-left: 34.25414364640884%;
693 | *margin-left: 34.14776066768544%;
694 | }
695 | .row-fluid .offset3 {
696 | margin-left: 28.45303867403315%;
697 | *margin-left: 28.346655695309746%;
698 | }
699 | .row-fluid .offset3:first-child {
700 | margin-left: 25.69060773480663%;
701 | *margin-left: 25.584224756083227%;
702 | }
703 | .row-fluid .offset2 {
704 | margin-left: 19.88950276243094%;
705 | *margin-left: 19.783119783707537%;
706 | }
707 | .row-fluid .offset2:first-child {
708 | margin-left: 17.12707182320442%;
709 | *margin-left: 17.02068884448102%;
710 | }
711 | .row-fluid .offset1 {
712 | margin-left: 11.32596685082873%;
713 | *margin-left: 11.219583872105325%;
714 | }
715 | .row-fluid .offset1:first-child {
716 | margin-left: 8.56353591160221%;
717 | *margin-left: 8.457152932878806%;
718 | }
719 | input,
720 | textarea,
721 | .uneditable-input {
722 | margin-left: 0;
723 | }
724 | .controls-row [class*="span"] + [class*="span"] {
725 | margin-left: 20px;
726 | }
727 | input.span12,
728 | textarea.span12,
729 | .uneditable-input.span12 {
730 | width: 710px;
731 | }
732 | input.span11,
733 | textarea.span11,
734 | .uneditable-input.span11 {
735 | width: 648px;
736 | }
737 | input.span10,
738 | textarea.span10,
739 | .uneditable-input.span10 {
740 | width: 586px;
741 | }
742 | input.span9,
743 | textarea.span9,
744 | .uneditable-input.span9 {
745 | width: 524px;
746 | }
747 | input.span8,
748 | textarea.span8,
749 | .uneditable-input.span8 {
750 | width: 462px;
751 | }
752 | input.span7,
753 | textarea.span7,
754 | .uneditable-input.span7 {
755 | width: 400px;
756 | }
757 | input.span6,
758 | textarea.span6,
759 | .uneditable-input.span6 {
760 | width: 338px;
761 | }
762 | input.span5,
763 | textarea.span5,
764 | .uneditable-input.span5 {
765 | width: 276px;
766 | }
767 | input.span4,
768 | textarea.span4,
769 | .uneditable-input.span4 {
770 | width: 214px;
771 | }
772 | input.span3,
773 | textarea.span3,
774 | .uneditable-input.span3 {
775 | width: 152px;
776 | }
777 | input.span2,
778 | textarea.span2,
779 | .uneditable-input.span2 {
780 | width: 90px;
781 | }
782 | input.span1,
783 | textarea.span1,
784 | .uneditable-input.span1 {
785 | width: 28px;
786 | }
787 | }
788 |
789 | @media (max-width: 767px) {
790 | body {
791 | padding-right: 20px;
792 | padding-left: 20px;
793 | }
794 | .navbar-fixed-top,
795 | .navbar-fixed-bottom,
796 | .navbar-static-top {
797 | margin-right: -20px;
798 | margin-left: -20px;
799 | }
800 | .container-fluid {
801 | padding: 0;
802 | }
803 | .dl-horizontal dt {
804 | float: none;
805 | width: auto;
806 | clear: none;
807 | text-align: left;
808 | }
809 | .dl-horizontal dd {
810 | margin-left: 0;
811 | }
812 | .container {
813 | width: auto;
814 | }
815 | .row-fluid {
816 | width: 100%;
817 | }
818 | .row,
819 | .thumbnails {
820 | margin-left: 0;
821 | }
822 | .thumbnails > li {
823 | float: none;
824 | margin-left: 0;
825 | }
826 | [class*="span"],
827 | .uneditable-input[class*="span"],
828 | .row-fluid [class*="span"] {
829 | display: block;
830 | float: none;
831 | width: 100%;
832 | margin-left: 0;
833 | -webkit-box-sizing: border-box;
834 | -moz-box-sizing: border-box;
835 | box-sizing: border-box;
836 | }
837 | .span12,
838 | .row-fluid .span12 {
839 | width: 100%;
840 | -webkit-box-sizing: border-box;
841 | -moz-box-sizing: border-box;
842 | box-sizing: border-box;
843 | }
844 | .row-fluid [class*="offset"]:first-child {
845 | margin-left: 0;
846 | }
847 | .input-large,
848 | .input-xlarge,
849 | .input-xxlarge,
850 | input[class*="span"],
851 | select[class*="span"],
852 | textarea[class*="span"],
853 | .uneditable-input {
854 | display: block;
855 | width: 100%;
856 | min-height: 30px;
857 | -webkit-box-sizing: border-box;
858 | -moz-box-sizing: border-box;
859 | box-sizing: border-box;
860 | }
861 | .input-prepend input,
862 | .input-append input,
863 | .input-prepend input[class*="span"],
864 | .input-append input[class*="span"] {
865 | display: inline-block;
866 | width: auto;
867 | }
868 | .controls-row [class*="span"] + [class*="span"] {
869 | margin-left: 0;
870 | }
871 | .modal {
872 | position: fixed;
873 | top: 20px;
874 | right: 20px;
875 | left: 20px;
876 | width: auto;
877 | margin: 0;
878 | }
879 | .modal.fade {
880 | top: -100px;
881 | }
882 | .modal.fade.in {
883 | top: 20px;
884 | }
885 | }
886 |
887 | @media (max-width: 480px) {
888 | .nav-collapse {
889 | -webkit-transform: translate3d(0, 0, 0);
890 | }
891 | .page-header h1 small {
892 | display: block;
893 | line-height: 20px;
894 | }
895 | input[type="checkbox"],
896 | input[type="radio"] {
897 | border: 1px solid #ccc;
898 | }
899 | .form-horizontal .control-label {
900 | float: none;
901 | width: auto;
902 | padding-top: 0;
903 | text-align: left;
904 | }
905 | .form-horizontal .controls {
906 | margin-left: 0;
907 | }
908 | .form-horizontal .control-list {
909 | padding-top: 0;
910 | }
911 | .form-horizontal .form-actions {
912 | padding-right: 10px;
913 | padding-left: 10px;
914 | }
915 | .media .pull-left,
916 | .media .pull-right {
917 | display: block;
918 | float: none;
919 | margin-bottom: 10px;
920 | }
921 | .media-object {
922 | margin-right: 0;
923 | margin-left: 0;
924 | }
925 | .modal {
926 | top: 10px;
927 | right: 10px;
928 | left: 10px;
929 | }
930 | .modal-header .close {
931 | padding: 10px;
932 | margin: -10px;
933 | }
934 | .carousel-caption {
935 | position: static;
936 | }
937 | }
938 |
939 | @media (max-width: 979px) {
940 | body {
941 | padding-top: 0;
942 | }
943 | .navbar-fixed-top,
944 | .navbar-fixed-bottom {
945 | position: static;
946 | }
947 | .navbar-fixed-top {
948 | margin-bottom: 20px;
949 | }
950 | .navbar-fixed-bottom {
951 | margin-top: 20px;
952 | }
953 | .navbar-fixed-top .navbar-inner,
954 | .navbar-fixed-bottom .navbar-inner {
955 | padding: 5px;
956 | }
957 | .navbar .container {
958 | width: auto;
959 | padding: 0;
960 | }
961 | .navbar .brand {
962 | padding-right: 10px;
963 | padding-left: 10px;
964 | margin: 0 0 0 -5px;
965 | }
966 | .nav-collapse {
967 | clear: both;
968 | }
969 | .nav-collapse .nav {
970 | float: none;
971 | margin: 0 0 10px;
972 | }
973 | .nav-collapse .nav > li {
974 | float: none;
975 | }
976 | .nav-collapse .nav > li > a {
977 | margin-bottom: 2px;
978 | }
979 | .nav-collapse .nav > .divider-vertical {
980 | display: none;
981 | }
982 | .nav-collapse .nav .nav-header {
983 | color: #777777;
984 | text-shadow: none;
985 | }
986 | .nav-collapse .nav > li > a,
987 | .nav-collapse .dropdown-menu a {
988 | padding: 9px 15px;
989 | font-weight: bold;
990 | color: #777777;
991 | -webkit-border-radius: 3px;
992 | -moz-border-radius: 3px;
993 | border-radius: 3px;
994 | }
995 | .nav-collapse .btn {
996 | padding: 4px 10px 4px;
997 | font-weight: normal;
998 | -webkit-border-radius: 4px;
999 | -moz-border-radius: 4px;
1000 | border-radius: 4px;
1001 | }
1002 | .nav-collapse .dropdown-menu li + li a {
1003 | margin-bottom: 2px;
1004 | }
1005 | .nav-collapse .nav > li > a:hover,
1006 | .nav-collapse .dropdown-menu a:hover {
1007 | background-color: #f2f2f2;
1008 | }
1009 | .navbar-inverse .nav-collapse .nav > li > a,
1010 | .navbar-inverse .nav-collapse .dropdown-menu a {
1011 | color: #999999;
1012 | }
1013 | .navbar-inverse .nav-collapse .nav > li > a:hover,
1014 | .navbar-inverse .nav-collapse .dropdown-menu a:hover {
1015 | background-color: #111111;
1016 | }
1017 | .nav-collapse.in .btn-group {
1018 | padding: 0;
1019 | margin-top: 5px;
1020 | }
1021 | .nav-collapse .dropdown-menu {
1022 | position: static;
1023 | top: auto;
1024 | left: auto;
1025 | display: none;
1026 | float: none;
1027 | max-width: none;
1028 | padding: 0;
1029 | margin: 0 15px;
1030 | background-color: transparent;
1031 | border: none;
1032 | -webkit-border-radius: 0;
1033 | -moz-border-radius: 0;
1034 | border-radius: 0;
1035 | -webkit-box-shadow: none;
1036 | -moz-box-shadow: none;
1037 | box-shadow: none;
1038 | }
1039 | .nav-collapse .open > .dropdown-menu {
1040 | display: block;
1041 | }
1042 | .nav-collapse .dropdown-menu:before,
1043 | .nav-collapse .dropdown-menu:after {
1044 | display: none;
1045 | }
1046 | .nav-collapse .dropdown-menu .divider {
1047 | display: none;
1048 | }
1049 | .nav-collapse .nav > li > .dropdown-menu:before,
1050 | .nav-collapse .nav > li > .dropdown-menu:after {
1051 | display: none;
1052 | }
1053 | .nav-collapse .navbar-form,
1054 | .nav-collapse .navbar-search {
1055 | float: none;
1056 | padding: 10px 15px;
1057 | margin: 10px 0;
1058 | border-top: 1px solid #f2f2f2;
1059 | border-bottom: 1px solid #f2f2f2;
1060 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1061 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1062 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1063 | }
1064 | .navbar-inverse .nav-collapse .navbar-form,
1065 | .navbar-inverse .nav-collapse .navbar-search {
1066 | border-top-color: #111111;
1067 | border-bottom-color: #111111;
1068 | }
1069 | .navbar .nav-collapse .nav.pull-right {
1070 | float: none;
1071 | margin-left: 0;
1072 | }
1073 | .nav-collapse,
1074 | .nav-collapse.collapse {
1075 | height: 0;
1076 | overflow: hidden;
1077 | }
1078 | .navbar .btn-navbar {
1079 | display: block;
1080 | }
1081 | .navbar-static .navbar-inner {
1082 | padding-right: 10px;
1083 | padding-left: 10px;
1084 | }
1085 | }
1086 |
1087 | @media (min-width: 980px) {
1088 | .nav-collapse.collapse {
1089 | height: auto !important;
1090 | overflow: visible !important;
1091 | }
1092 | }
1093 |
--------------------------------------------------------------------------------
/example/lib/bootstrap/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jimrhoskins/angular-parse/fac11b2c7d29020c4ddabf2504841f968be25bd0/example/lib/bootstrap/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/example/lib/bootstrap/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jimrhoskins/angular-parse/fac11b2c7d29020c4ddabf2504841f968be25bd0/example/lib/bootstrap/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/example/parse/cloud/main.js:
--------------------------------------------------------------------------------
1 |
2 | Parse.Cloud.define("hello", function(request, response) {
3 | return response.success("Hello coffee!");
4 | });
5 |
6 | Parse.Cloud.beforeSave('Post', function(req, res) {
7 | var post, user;
8 | post = req.object;
9 | user = req.user;
10 | if (!user) {
11 | return res.error("You must be signed in to post.");
12 | }
13 | if (!post.get("title").length) {
14 | return res.error("You must include a title");
15 | }
16 | if (!post.get("body").length) {
17 | return res.error("You must include a body");
18 | }
19 | post.set('commentCount', post.get('commentCount') || 0);
20 | post.set('author', user);
21 | return res.success();
22 | });
23 |
24 | Parse.Cloud.beforeSave('Comment', function(req, res) {
25 | var comment, user;
26 | comment = req.object;
27 | user = req.user;
28 | if (!user) {
29 | return res.error("You must be signed in to post.");
30 | }
31 | if (!comment.get("body").length) {
32 | return res.error("You must include a body");
33 | }
34 | comment.set('author', user);
35 | return res.success();
36 | });
37 |
--------------------------------------------------------------------------------
/example/parse/config/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "applications": {
3 | "Forum": {
4 | "applicationId": "x58FvFYxi4SDIRdJck4hFVv1huo8F409UnfERfUU",
5 | "masterKey": "NdmAC7MEaW1v8wzYyO6z8hqiyvmqPIcJKgeargKj"
6 | },
7 | "_default": {
8 | "link": "Forum"
9 | }
10 | },
11 | "global": {
12 | "parseVersion": "1.1.15"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example/parse/src/main.coffee:
--------------------------------------------------------------------------------
1 |
2 | Parse.Cloud.define "hello", (request, response) ->
3 | response.success("Hello coffee!")
4 |
5 | Parse.Cloud.beforeSave 'Post', (req, res) ->
6 | post = req.object
7 | user = req.user
8 |
9 | unless user
10 | return res.error "You must be signed in to post."
11 |
12 | unless post.get("title").length
13 | return res.error "You must include a title"
14 |
15 | unless post.get("body").length
16 | return res.error "You must include a body"
17 |
18 | post.set('commentCount', post.get('commentCount') || 0)
19 | post.set('author', user)
20 |
21 | res.success()
22 |
23 | Parse.Cloud.beforeSave 'Comment', (req, res) ->
24 | comment = req.object
25 | user = req.user
26 |
27 | unless user
28 | return res.error "You must be signed in to post."
29 |
30 | unless comment.get("body").length
31 | return res.error "You must include a body"
32 |
33 | comment.set('author', user)
34 |
35 | res.success()
36 |
37 |
--------------------------------------------------------------------------------
/example/partials/detail.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
{{post.title}}
7 |
8 | Asked by
{{post.author.username}}
9 |
10 | {{post.body}}
11 |
12 |
13 |
14 | {{tag}}
15 |
16 |
17 |
Comments
18 |
32 |
39 |
40 |
--------------------------------------------------------------------------------
/example/partials/form.html:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/example/partials/list.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
11 |
12 |
13 | Avatar
14 |
15 |
24 |
27 |
28 | {{cars}}
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/partials/register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/example/partials/sign-in.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Testacular configuration
2 | // Generated on Tue Dec 18 2012 20:42:55 GMT-0500 (EST)
3 |
4 |
5 | // base path, that will be used to resolve files and exclude
6 | basePath = '.';
7 |
8 | preprocessors = {
9 | "**/*.coffee": "coffee"
10 | }
11 |
12 | // list of files / patterns to load in the browser
13 | files = [
14 | JASMINE,
15 | JASMINE_ADAPTER,
16 | 'lib/angular/angular.js',
17 | 'lib/angular/angular-*.js',
18 | 'test/support/angular-mocks.js',
19 | 'test/support/jasmine.async.js',
20 | 'angular-parse.js',
21 | 'test/unit/**/*.coffee'
22 | ];
23 |
24 |
25 | // list of files to exclude
26 | exclude = [
27 |
28 | ];
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit'
33 | reporters = ['progress'];
34 |
35 |
36 | // web server port
37 | port = 9101;
38 |
39 |
40 | // cli runner port
41 | runnerPort = 9100;
42 |
43 |
44 | // enable / disable colors in the output (reporters and logs)
45 | colors = true;
46 |
47 |
48 | // level of logging
49 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
50 | logLevel = LOG_INFO;
51 |
52 |
53 | // enable / disable watching file and executing tests whenever any file changes
54 | autoWatch = true;
55 |
56 |
57 | // Start these browsers, currently available:
58 | // - Chrome
59 | // - ChromeCanary
60 | // - Firefox
61 | // - Opera
62 | // - Safari (only Mac)
63 | // - PhantomJS
64 | // - IE (only Windows)
65 | browsers = ['PhantomJS'];
66 |
67 |
68 | // If browser does not capture in given timeout [ms], kill it
69 | captureTimeout = 5000;
70 |
71 |
72 | // Continuous Integration mode
73 | // if true, it capture browsers, run tests and exit
74 | singleRun = false;
75 |
--------------------------------------------------------------------------------
/lib/angular/angular-cookies.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.2
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {
7 | 'use strict';
8 |
9 | /**
10 | * @ngdoc overview
11 | * @name ngCookies
12 | */
13 |
14 |
15 | angular.module('ngCookies', ['ng']).
16 | /**
17 | * @ngdoc object
18 | * @name ngCookies.$cookies
19 | * @requires $browser
20 | *
21 | * @description
22 | * Provides read/write access to browser's cookies.
23 | *
24 | * Only a simple Object is exposed and by adding or removing properties to/from
25 | * this object, new cookies are created/deleted at the end of current $eval.
26 | *
27 | * @example
28 | */
29 | factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
30 | var cookies = {},
31 | lastCookies = {},
32 | lastBrowserCookies,
33 | runEval = false,
34 | copy = angular.copy,
35 | isUndefined = angular.isUndefined;
36 |
37 | //creates a poller fn that copies all cookies from the $browser to service & inits the service
38 | $browser.addPollFn(function() {
39 | var currentCookies = $browser.cookies();
40 | if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
41 | lastBrowserCookies = currentCookies;
42 | copy(currentCookies, lastCookies);
43 | copy(currentCookies, cookies);
44 | if (runEval) $rootScope.$apply();
45 | }
46 | })();
47 |
48 | runEval = true;
49 |
50 | //at the end of each eval, push cookies
51 | //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
52 | // strings or browser refuses to store some cookies, we update the model in the push fn.
53 | $rootScope.$watch(push);
54 |
55 | return cookies;
56 |
57 |
58 | /**
59 | * Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
60 | */
61 | function push() {
62 | var name,
63 | value,
64 | browserCookies,
65 | updated;
66 |
67 | //delete any cookies deleted in $cookies
68 | for (name in lastCookies) {
69 | if (isUndefined(cookies[name])) {
70 | $browser.cookies(name, undefined);
71 | }
72 | }
73 |
74 | //update all cookies updated in $cookies
75 | for(name in cookies) {
76 | value = cookies[name];
77 | if (!angular.isString(value)) {
78 | if (angular.isDefined(lastCookies[name])) {
79 | cookies[name] = lastCookies[name];
80 | } else {
81 | delete cookies[name];
82 | }
83 | } else if (value !== lastCookies[name]) {
84 | $browser.cookies(name, value);
85 | updated = true;
86 | }
87 | }
88 |
89 | //verify what was actually stored
90 | if (updated){
91 | updated = false;
92 | browserCookies = $browser.cookies();
93 |
94 | for (name in cookies) {
95 | if (cookies[name] !== browserCookies[name]) {
96 | //delete or reset all cookies that the browser dropped from $cookies
97 | if (isUndefined(browserCookies[name])) {
98 | delete cookies[name];
99 | } else {
100 | cookies[name] = browserCookies[name];
101 | }
102 | updated = true;
103 | }
104 | }
105 | }
106 | }
107 | }]).
108 |
109 |
110 | /**
111 | * @ngdoc object
112 | * @name ngCookies.$cookieStore
113 | * @requires $cookies
114 | *
115 | * @description
116 | * Provides a key-value (string-object) storage, that is backed by session cookies.
117 | * Objects put or retrieved from this storage are automatically serialized or
118 | * deserialized by angular's toJson/fromJson.
119 | * @example
120 | */
121 | factory('$cookieStore', ['$cookies', function($cookies) {
122 |
123 | return {
124 | /**
125 | * @ngdoc method
126 | * @name ngCookies.$cookieStore#get
127 | * @methodOf ngCookies.$cookieStore
128 | *
129 | * @description
130 | * Returns the value of given cookie key
131 | *
132 | * @param {string} key Id to use for lookup.
133 | * @returns {Object} Deserialized cookie value.
134 | */
135 | get: function(key) {
136 | return angular.fromJson($cookies[key]);
137 | },
138 |
139 | /**
140 | * @ngdoc method
141 | * @name ngCookies.$cookieStore#put
142 | * @methodOf ngCookies.$cookieStore
143 | *
144 | * @description
145 | * Sets a value for given cookie key
146 | *
147 | * @param {string} key Id for the `value`.
148 | * @param {Object} value Value to be stored.
149 | */
150 | put: function(key, value) {
151 | $cookies[key] = angular.toJson(value);
152 | },
153 |
154 | /**
155 | * @ngdoc method
156 | * @name ngCookies.$cookieStore#remove
157 | * @methodOf ngCookies.$cookieStore
158 | *
159 | * @description
160 | * Remove given cookie
161 | *
162 | * @param {string} key Id of the key-value pair to delete.
163 | */
164 | remove: function(key) {
165 | delete $cookies[key];
166 | }
167 | };
168 |
169 | }]);
170 |
171 | })(window, window.angular);
172 |
--------------------------------------------------------------------------------
/lib/angular/angular-cookies.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.2
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,c){var b={},g={},h,i=!1,j=f.copy,k=f.isUndefined;c.addPollFn(function(){var a=c.cookies();h!=a&&(h=a,j(a,g),j(a,b),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(b[a])&&c.cookies(a,l);for(a in b)e=b[a],f.isString(e)?e!==g[a]&&(c.cookies(a,e),d=!0):f.isDefined(g[a])?b[a]=g[a]:delete b[a];if(d)for(a in e=c.cookies(),b)b[a]!==e[a]&&(k(e[a])?delete b[a]:b[a]=e[a])});return b}]).factory("$cookieStore",
7 | ["$cookies",function(d){return{get:function(c){return f.fromJson(d[c])},put:function(c,b){d[c]=f.toJson(b)},remove:function(c){delete d[c]}}}])})(window,window.angular);
8 |
--------------------------------------------------------------------------------
/lib/angular/angular-loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.2
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 |
7 | (
8 |
9 | /**
10 | * @ngdoc interface
11 | * @name angular.Module
12 | * @description
13 | *
14 | * Interface for configuring angular {@link angular.module modules}.
15 | */
16 |
17 | function setupModuleLoader(window) {
18 |
19 | function ensure(obj, name, factory) {
20 | return obj[name] || (obj[name] = factory());
21 | }
22 |
23 | return ensure(ensure(window, 'angular', Object), 'module', function() {
24 | /** @type {Object.
} */
25 | var modules = {};
26 |
27 | /**
28 | * @ngdoc function
29 | * @name angular.module
30 | * @description
31 | *
32 | * The `angular.module` is a global place for creating and registering Angular modules. All
33 | * modules (angular core or 3rd party) that should be available to an application must be
34 | * registered using this mechanism.
35 | *
36 | *
37 | * # Module
38 | *
39 | * A module is a collocation of services, directives, filters, and configure information. Module
40 | * is used to configure the {@link AUTO.$injector $injector}.
41 | *
42 | *
43 | * // Create a new module
44 | * var myModule = angular.module('myModule', []);
45 | *
46 | * // register a new service
47 | * myModule.value('appName', 'MyCoolApp');
48 | *
49 | * // configure existing services inside initialization blocks.
50 | * myModule.config(function($locationProvider) {
51 | 'use strict';
52 | * // Configure existing providers
53 | * $locationProvider.hashPrefix('!');
54 | * });
55 | *
56 | *
57 | * Then you can create an injector and load your modules like this:
58 | *
59 | *
60 | * var injector = angular.injector(['ng', 'MyModule'])
61 | *
62 | *
63 | * However it's more likely that you'll just use
64 | * {@link ng.directive:ngApp ngApp} or
65 | * {@link angular.bootstrap} to simplify this process for you.
66 | *
67 | * @param {!string} name The name of the module to create or retrieve.
68 | * @param {Array.=} requires If specified then new module is being created. If unspecified then the
69 | * the module is being retrieved for further configuration.
70 | * @param {Function} configFn Option configuration function for the module. Same as
71 | * {@link angular.Module#config Module#config()}.
72 | * @returns {module} new module with the {@link angular.Module} api.
73 | */
74 | return function module(name, requires, configFn) {
75 | if (requires && modules.hasOwnProperty(name)) {
76 | modules[name] = null;
77 | }
78 | return ensure(modules, name, function() {
79 | if (!requires) {
80 | throw Error('No module: ' + name);
81 | }
82 |
83 | /** @type {!Array.>} */
84 | var invokeQueue = [];
85 |
86 | /** @type {!Array.} */
87 | var runBlocks = [];
88 |
89 | var config = invokeLater('$injector', 'invoke');
90 |
91 | /** @type {angular.Module} */
92 | var moduleInstance = {
93 | // Private state
94 | _invokeQueue: invokeQueue,
95 | _runBlocks: runBlocks,
96 |
97 | /**
98 | * @ngdoc property
99 | * @name angular.Module#requires
100 | * @propertyOf angular.Module
101 | * @returns {Array.} List of module names which must be loaded before this module.
102 | * @description
103 | * Holds the list of modules which the injector will load before the current module is loaded.
104 | */
105 | requires: requires,
106 |
107 | /**
108 | * @ngdoc property
109 | * @name angular.Module#name
110 | * @propertyOf angular.Module
111 | * @returns {string} Name of the module.
112 | * @description
113 | */
114 | name: name,
115 |
116 |
117 | /**
118 | * @ngdoc method
119 | * @name angular.Module#provider
120 | * @methodOf angular.Module
121 | * @param {string} name service name
122 | * @param {Function} providerType Construction function for creating new instance of the service.
123 | * @description
124 | * See {@link AUTO.$provide#provider $provide.provider()}.
125 | */
126 | provider: invokeLater('$provide', 'provider'),
127 |
128 | /**
129 | * @ngdoc method
130 | * @name angular.Module#factory
131 | * @methodOf angular.Module
132 | * @param {string} name service name
133 | * @param {Function} providerFunction Function for creating new instance of the service.
134 | * @description
135 | * See {@link AUTO.$provide#factory $provide.factory()}.
136 | */
137 | factory: invokeLater('$provide', 'factory'),
138 |
139 | /**
140 | * @ngdoc method
141 | * @name angular.Module#service
142 | * @methodOf angular.Module
143 | * @param {string} name service name
144 | * @param {Function} constructor A constructor function that will be instantiated.
145 | * @description
146 | * See {@link AUTO.$provide#service $provide.service()}.
147 | */
148 | service: invokeLater('$provide', 'service'),
149 |
150 | /**
151 | * @ngdoc method
152 | * @name angular.Module#value
153 | * @methodOf angular.Module
154 | * @param {string} name service name
155 | * @param {*} object Service instance object.
156 | * @description
157 | * See {@link AUTO.$provide#value $provide.value()}.
158 | */
159 | value: invokeLater('$provide', 'value'),
160 |
161 | /**
162 | * @ngdoc method
163 | * @name angular.Module#constant
164 | * @methodOf angular.Module
165 | * @param {string} name constant name
166 | * @param {*} object Constant value.
167 | * @description
168 | * Because the constant are fixed, they get applied before other provide methods.
169 | * See {@link AUTO.$provide#constant $provide.constant()}.
170 | */
171 | constant: invokeLater('$provide', 'constant', 'unshift'),
172 |
173 | /**
174 | * @ngdoc method
175 | * @name angular.Module#filter
176 | * @methodOf angular.Module
177 | * @param {string} name Filter name.
178 | * @param {Function} filterFactory Factory function for creating new instance of filter.
179 | * @description
180 | * See {@link ng.$filterProvider#register $filterProvider.register()}.
181 | */
182 | filter: invokeLater('$filterProvider', 'register'),
183 |
184 | /**
185 | * @ngdoc method
186 | * @name angular.Module#controller
187 | * @methodOf angular.Module
188 | * @param {string} name Controller name.
189 | * @param {Function} constructor Controller constructor function.
190 | * @description
191 | * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
192 | */
193 | controller: invokeLater('$controllerProvider', 'register'),
194 |
195 | /**
196 | * @ngdoc method
197 | * @name angular.Module#directive
198 | * @methodOf angular.Module
199 | * @param {string} name directive name
200 | * @param {Function} directiveFactory Factory function for creating new instance of
201 | * directives.
202 | * @description
203 | * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
204 | */
205 | directive: invokeLater('$compileProvider', 'directive'),
206 |
207 | /**
208 | * @ngdoc method
209 | * @name angular.Module#config
210 | * @methodOf angular.Module
211 | * @param {Function} configFn Execute this function on module load. Useful for service
212 | * configuration.
213 | * @description
214 | * Use this method to register work which needs to be performed on module loading.
215 | */
216 | config: config,
217 |
218 | /**
219 | * @ngdoc method
220 | * @name angular.Module#run
221 | * @methodOf angular.Module
222 | * @param {Function} initializationFn Execute this function after injector creation.
223 | * Useful for application initialization.
224 | * @description
225 | * Use this method to register work which needs to be performed when the injector with
226 | * with the current module is finished loading.
227 | */
228 | run: function(block) {
229 | runBlocks.push(block);
230 | return this;
231 | }
232 | };
233 |
234 | if (configFn) {
235 | config(configFn);
236 | }
237 |
238 | return moduleInstance;
239 |
240 | /**
241 | * @param {string} provider
242 | * @param {string} method
243 | * @param {String=} insertMethod
244 | * @returns {angular.Module}
245 | */
246 | function invokeLater(provider, method, insertMethod) {
247 | return function() {
248 | invokeQueue[insertMethod || 'push']([provider, method, arguments]);
249 | return moduleInstance;
250 | }
251 | }
252 | });
253 | };
254 | });
255 |
256 | }
257 | )(window);
258 |
259 | /**
260 | * Closure compiler type information
261 | *
262 | * @typedef { {
263 | * requires: !Array.,
264 | * invokeQueue: !Array.>,
265 | *
266 | * service: function(string, Function):angular.Module,
267 | * factory: function(string, Function):angular.Module,
268 | * value: function(string, *):angular.Module,
269 | *
270 | * filter: function(string, Function):angular.Module,
271 | *
272 | * init: function(Function):angular.Module
273 | * } }
274 | */
275 | angular.Module;
276 |
277 |
--------------------------------------------------------------------------------
/lib/angular/angular-loader.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.2
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(i){'use strict';function d(c,b,e){return c[b]||(c[b]=e())}return d(d(i,"angular",Object),"module",function(){var c={};return function(b,e,f){e&&c.hasOwnProperty(b)&&(c[b]=null);return d(c,b,function(){function a(a,b,d){return function(){c[d||"push"]([a,b,arguments]);return g}}if(!e)throw Error("No module: "+b);var c=[],d=[],h=a("$injector","invoke"),g={_invokeQueue:c,_runBlocks:d,requires:e,name:b,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),
7 | value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:h,run:function(a){d.push(a);return this}};f&&h(f);return g})}})})(window);
8 |
--------------------------------------------------------------------------------
/lib/angular/angular-resource.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.2
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {
7 | 'use strict';
8 |
9 | /**
10 | * @ngdoc overview
11 | * @name ngResource
12 | * @description
13 | */
14 |
15 | /**
16 | * @ngdoc object
17 | * @name ngResource.$resource
18 | * @requires $http
19 | *
20 | * @description
21 | * A factory which creates a resource object that lets you interact with
22 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
23 | *
24 | * The returned resource object has action methods which provide high-level behaviors without
25 | * the need to interact with the low level {@link ng.$http $http} service.
26 | *
27 | * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
28 | * `/user/:username`.
29 | *
30 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
31 | * `actions` methods.
32 | *
33 | * Each key value in the parameter object is first bound to url template if present and then any
34 | * excess keys are appended to the url search query after the `?`.
35 | *
36 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
37 | * URL `/path/greet?salutation=Hello`.
38 | *
39 | * If the parameter value is prefixed with `@` then the value of that parameter is extracted from
40 | * the data object (useful for non-GET operations).
41 | *
42 | * @param {Object.