chr 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +6 -3
  3. data/LICENSE.md +1 -1
  4. data/README.md +286 -7
  5. data/app/assets/javascripts/chr-dist.js +1031 -580
  6. data/app/assets/javascripts/chr.coffee +7 -4
  7. data/app/assets/javascripts/chr/core/chr.coffee +88 -37
  8. data/app/assets/javascripts/chr/core/item.coffee +57 -35
  9. data/app/assets/javascripts/chr/core/{list-scroll.coffee → list-pagination.coffee} +6 -3
  10. data/app/assets/javascripts/chr/core/list-reorder.coffee +3 -3
  11. data/app/assets/javascripts/chr/core/list-search.coffee +20 -11
  12. data/app/assets/javascripts/chr/core/list.coffee +163 -89
  13. data/app/assets/javascripts/chr/core/module.coffee +75 -35
  14. data/app/assets/javascripts/chr/core/view.coffee +117 -61
  15. data/app/assets/javascripts/chr/store/{store.coffee → _array-store.coffee} +53 -106
  16. data/app/assets/javascripts/chr/store/_object-store.coffee +28 -0
  17. data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +199 -0
  18. data/app/assets/javascripts/chr/store/mongosteen-object-store.coffee +52 -0
  19. data/app/assets/javascripts/chr/store/rest-array-store.coffee +142 -0
  20. data/app/assets/javascripts/chr/store/rest-object-store.coffee +79 -0
  21. data/app/assets/stylesheets/core/_list.scss +20 -25
  22. data/app/assets/stylesheets/core/_main.scss +3 -4
  23. data/app/assets/stylesheets/core/_mixins.scss +33 -2
  24. data/app/assets/stylesheets/core/_responsive.scss +30 -9
  25. data/app/assets/stylesheets/form/_input_checkbox.scss +18 -14
  26. data/bower.json +3 -2
  27. data/chr.gemspec +1 -1
  28. data/docs/assets.md +0 -0
  29. data/docs/basics.md +0 -0
  30. data/docs/bootstrap-data.md +0 -0
  31. data/docs/custom-inputs.md +0 -0
  32. data/docs/form.md +0 -0
  33. data/docs/internals.md +0 -0
  34. data/docs/nested-forms.md +0 -0
  35. data/docs/redactor-js.md +0 -0
  36. data/docs/scopes.md +0 -0
  37. data/lib/chr/version.rb +1 -1
  38. data/package.json +1 -1
  39. metadata +19 -7
  40. data/app/assets/javascripts/chr/store/store-mongosteen.coffee +0 -111
  41. data/app/assets/javascripts/chr/store/store-rest.coffee +0 -90
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9e6bbd63bec5db5e3e4f86134f26d13086c88d15
4
- data.tar.gz: 041a2c987d9253e3c579fe1f059c3d83a4960763
3
+ metadata.gz: a6fea1a403df7fa8fb67b865cd4be5a7e7c4ad83
4
+ data.tar.gz: f37f39a6e5085f4d9acf6562302d63c83f8a022a
5
5
  SHA512:
6
- metadata.gz: 82ac289396d965e246640641926cb853c74622cb300b806bfb9ec5b852c63cb126693b2a042da804527353dd2d5c6cf18d20ca29adf352c07fd3a72cbc545982
7
- data.tar.gz: edc30ec3c9bba84f3595a8167e44f909276336b3972f552166d63a01b7959f1284ac4e74697b246912f14b7de8bb284415a22c6c0c7e70d2fa9af5fee6a61303
6
+ metadata.gz: 219e1d5a073982bd939ec2dac02b9da07b79af1c7e4edafe5a8f351ae17b73ecdbc964bfa6f6dea85ec69852ca855a39f62a840036c40927573a8a97ac51af7f
7
+ data.tar.gz: e0681fa1a0689875d3f127aa86af2bfe2b26492cb5403a2124a4027e6231ba2dabbb66bcef03bec1ae5d7f197d28b1a3a5e7cbb4ba3986bd2a9aa71c5cd415ab
@@ -28,9 +28,12 @@ module.exports = (grunt) ->
28
28
  'app/assets/javascripts/chr/form/input-text.coffee'
29
29
  'app/assets/javascripts/chr/form/nested-form.coffee'
30
30
  #
31
- 'app/assets/javascripts/chr/store/store.coffee'
32
- 'app/assets/javascripts/chr/store/store-rest.coffee'
33
- 'app/assets/javascripts/chr/store/store-mongosteen.coffee'
31
+ 'app/assets/javascripts/chr/store/_array-store.coffee'
32
+ 'app/assets/javascripts/chr/store/_object-store.coffee'
33
+ 'app/assets/javascripts/chr/store/rest-array-store.coffee'
34
+ 'app/assets/javascripts/chr/store/rest-object-store.coffee'
35
+ 'app/assets/javascripts/chr/store/mongosteen-array-store.coffee'
36
+ 'app/assets/javascripts/chr/store/mongosteen-object-store.coffee'
34
37
  #
35
38
  'app/assets/javascripts/chr/core/utils.coffee'
36
39
  'app/assets/javascripts/chr/core/chr.coffee'
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright © 2015 [slatestudio, inc.](http://slatestudio.com)
3
+ Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the “Software”), to deal
data/README.md CHANGED
@@ -1,17 +1,296 @@
1
1
  # Character
2
2
 
3
- ## A simple and lightweight library for building data management web apps
3
+ *Powerful responsive javascript CMS for apps.*
4
4
 
5
- ## The Character family
6
5
 
7
- - [Mongosteen](https://github.com/slate-studio/mongosteen): An easy way to add restful actions for mongoid models
6
+ ## Rails
8
7
 
9
- ## Credits
8
+ An example of admin implementation setup for [Rails](https://github.com/rails/rails) app that uses [Mongoid](https://github.com/mongoid/mongoid) stack.
10
9
 
11
- [![Slate Studio](https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png)](http://slatestudio.com)
12
10
 
13
- Character is maintained and funded by [Slate Studio, LLC](http://slatestudio.com). Tweet your questions or suggestions to [@slatestudio](https://twitter.com/slatestudio) and while you’re at it follow us too.
11
+ #### Gems
12
+
13
+ Add to following gems to ```Gemfile```:
14
+
15
+ gem "devise"
16
+ gem "mongosteen"
17
+ gem "chr"
18
+
19
+ This example uses ```devise``` for admins authentication.
20
+
21
+
22
+ #### Admin authentication
23
+
24
+ Start with running [devise](https://github.com/plataformatec/devise) generator:
25
+
26
+ rails generate devise:install
27
+
28
+ Setup ```Admin``` model with devise generator:
29
+
30
+ rails generate devise admin
31
+
32
+ Here is an example of basic ```app/models/admin.rb``` model that provides email/password authentication:
33
+
34
+ ```ruby
35
+ class Admin
36
+ include Mongoid::Document
37
+ include Mongoid::Timestamps
38
+ include Mongoid::SerializableId
39
+
40
+ devise :database_authenticatable,
41
+ :rememberable,
42
+ :authentication_keys => [ :email ]
43
+
44
+ ## Database authenticatable
45
+ field :email, type: String, default: ""
46
+ field :encrypted_password, type: String, default: ""
47
+
48
+ ## Rememberable
49
+ field :remember_created_at, type: Time
50
+ end
51
+ ```
52
+
53
+ When models are ready, setup controllers, views and configure routes.
54
+
55
+ Base admin controller ```app/controllers/admin/base_controller.rb``` looks like this:
56
+
57
+ ```ruby
58
+ class Admin::BaseController < ActionController::Base
59
+ protect_from_forgery
60
+
61
+ if Rails.env.production?
62
+ before_action :authenticate_admin!
63
+ end
64
+
65
+ def index
66
+ render '/admin/index', layout: 'admin'
67
+ end
68
+
69
+ def bootstrap_data
70
+ render json: {}
71
+ end
72
+ end
73
+ ```
74
+
75
+ Notes on code above:
76
+
77
+ 1. Authentication is not required when running in development or testing environment;
78
+ 2. Need to setup ```index``` view and ```admin``` layout to render admin app;
79
+ 3. ```bootstrap_data``` is a placeholder for objects that might be required to be loaded when app starts.
80
+
81
+ Devise would require a custom ```SessionController``` implementation in ```app/controllers/admin/devise_overrides/session_controller.rb```. ```SessionController``` sets ```admin``` layout to be used for devise views rendering and enables login by email (*looks like workaround*).
82
+
83
+ ```ruby
84
+ class Admin::DeviseOverrides::SessionsController < Devise::SessionsController
85
+ layout 'admin'
86
+
87
+ protected
88
+
89
+ def configure_permitted_parameters
90
+ devise_parameter_sanitizer.for(:sign_in) << :email
91
+ end
92
+ end
93
+ ```
94
+
95
+ Admin app layout ```app/views/layouts/admin.html.erb```:
96
+
97
+ ```erb
98
+ <!doctype html>
99
+ <html>
100
+ <head>
101
+ <meta charset="utf-8">
102
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
103
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
104
+ <meta name="apple-mobile-web-app-capable" content="yes">
105
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
106
+ <title>Admin</title>
107
+ <%= csrf_meta_tags %>
108
+ <%= stylesheet_link_tag :admin, media: "all" %>
109
+ <%= javascript_include_tag :admin %>
110
+ </head>
111
+
112
+ <%= yield %>
113
+ </html>
114
+ ```
115
+
116
+ Admin index view ```app/views/admin/index.html.erb```:
117
+
118
+ ```erb
119
+ <body class='loading'>
120
+ <%= link_to 'Sign Out', destroy_admin_session_path, method: :delete, style: 'display:none;' %>
121
+ </body>
122
+ ```
123
+
124
+ New session view for devise ```app/views/admin/devise_overrides/sessions/new.html.erb```:
125
+
126
+ ```erb
127
+ <body class='sign-in'>
128
+ <h2>Sign In</h2>
129
+
130
+ <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
131
+ <% if alert %>
132
+ <p class="error"><%= alert.gsub('username', 'email').gsub('or sign up', '') %></p>
133
+ <% end %>
134
+
135
+ <div class="form-inputs">
136
+ <%= f.input :email, required: true, autofocus: true %>
137
+ <%= f.input :password, required: true %>
138
+
139
+ <%= f.input :remember_me, as: :boolean if devise_mapping.rememberable? %>
140
+ </div>
141
+
142
+ <div class="form-actions">
143
+ <%= f.button :submit, "Sign In" %>
144
+ </div>
145
+ <% end %>
146
+ </body>
147
+ ```
148
+
149
+ Now connect admin and devise in ```config/routes.rb``` with:
150
+
151
+ ```ruby
152
+ devise_for :admins, path: "admin", controllers: { sessions: "admin/devise_overrides/sessions" }
153
+ namespace :admin do
154
+ get '/' => 'base#index'
155
+ get '/bootstrap.json' => 'base#bootstrap_data'
156
+ end
157
+ ```
158
+
159
+
160
+ #### Character setup
161
+
162
+ Three pieces to be configured here.
163
+
164
+ **First**: create ```app/assets/javascripts/admin.coffee``` with empty ```modules``` configuration:
165
+
166
+ ```coffee
167
+ #= require jquery
168
+ #= require jquery_ujs
169
+ #= require chr
170
+
171
+ $ ->
172
+ $.get '/admin/bootstrap.json', (response) ->
173
+ config =
174
+ modules: {}
175
+
176
+ $('body').removeClass('loading')
177
+ chr.start(config)
178
+
179
+ # append signout button to the end of sidebar menu
180
+ $('a[data-method=delete]').appendTo(".sidebar .menu").show()
181
+ ```
182
+
183
+ **Second**: create foundation for style customization in ```app/assets/stylesheets/admin.scss```:
184
+
185
+ ```scss
186
+ @charset "utf-8";
187
+
188
+ @import "normalize-rails";
189
+ @import "chr";
190
+ @import "admin/signin";
191
+ ```
192
+
193
+ Last import in the code above is optional. But here is a default source for it as well ```app/assets/stylesheets/admin/chr/_signin.scss```:
194
+
195
+ ```scss
196
+ .sign-in {
197
+ font-size: 14px;
198
+ color: #555;
199
+ margin: 3em 0 0 3em;
200
+
201
+ h2 {
202
+ text-transform: uppercase;
203
+ font-size: 1em;
204
+ font-size: 16px;
205
+ color: $black;
206
+ margin-bottom: 1.5em;
207
+ }
208
+
209
+ p {
210
+ margin: -1.5em 0 2em;
211
+ color: $positiveColor;
212
+ }
213
+
214
+ .form-actions, .form-inputs {
215
+ max-width: 280px;
216
+ }
217
+
218
+ .input {
219
+ margin-bottom: 1.5em;
220
+ }
221
+
222
+ input.string, input.password {
223
+ float: right;
224
+ margin-top: -.45em;
225
+ padding: .25em .5em;
226
+ width: 13.5em;
227
+ }
228
+
229
+ label.boolean input {
230
+ margin-right: .25em;
231
+ }
232
+
233
+ .form-actions input {
234
+ width: 100%;
235
+ padding: 1em 2em;
236
+ background-color: $positiveColor;
237
+ border: 0;
238
+ color: $white;
239
+ }
240
+ }
241
+ ```
242
+
243
+ **Third**: make sure admin assets are precompiled on production, include ```admin.js``` and ```admin.css``` in ```config/initializers/assets.rb```:
244
+
245
+ ```ruby
246
+ Rails.application.config.assets.precompile += %w( admin.js admin.css )
247
+ ```
248
+
249
+ At this point initial setup for admin app is finished and it could be accessed via: ```localhost:3000/admin```.
250
+
251
+
252
+ #### Add models
253
+
254
+ To be continued...
255
+
256
+
257
+ #### Bootstrap data
258
+
259
+ Bootstrapped data configuration example with disabled item updates and pagination:
260
+
261
+ ```coffee
262
+ postsConfig = (data) ->
263
+ itemTitleField: 'title'
264
+ disableUpdateItems: true
265
+ objects: data.posts
266
+ arrayStore: new MongosteenArrayStore({
267
+ resource: 'post'
268
+ path: '/admin/posts'
269
+ sortBy: 'title'
270
+ pagination: false
271
+ })
272
+ formSchema:
273
+ title: { type: 'string' }
274
+ body: { type: 'text' }
275
+ ```
276
+
277
+ ```disableUpdateItems``` — do not update items in the list while navigation, ```objects``` — provides initial (bootstrapped) array of objects to be added to the list, ```pagination``` — disable pagination for list. If attached as modules root list, you can access store data with: ```chr.modules.posts.arrayStore.data()```.
278
+
279
+
280
+ ## Character tastes better with:
281
+
282
+ - [Mongosteen](https://github.com/slate-studio/mongosteen): An easy way to add restful actions for Mongoid models
283
+ - [Inverter](https://github.com/slate-studio/inverter): An easy way to connect Rails templates content to Character CMS
284
+ - [Loft](https://github.com/slate-studio/loft): Media assets manager for Character CMS
285
+
14
286
 
15
287
  ## License
16
288
 
17
- Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com). Character is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
289
+ Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com). Character is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
290
+
291
+
292
+ ## About Slate Studio
293
+
294
+ [![Slate Studio](https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png)](http://slatestudio.com)
295
+
296
+ Character is maintained and funded by [Slate Studio, LLC](http://slatestudio.com). Tweet your questions or suggestions to [@slatestudio](https://twitter.com/slatestudio) and while you’re at it follow us too.
@@ -2655,7 +2655,41 @@ jQuery.fn.scrollParent = function() {
2655
2655
  })();
2656
2656
  })(window.jQuery);
2657
2657
  this.Item = (function() {
2658
- Item.prototype._isFolder = function() {
2658
+ function Item(module, path, object, config) {
2659
+ this.module = module;
2660
+ this.path = path;
2661
+ this.object = object;
2662
+ this.config = config;
2663
+ this.$el = $("<a class='item' href='" + this.path + "' data-id='" + this.object._id + "' data-title=''></a>");
2664
+ this.$el.on('click', (function(_this) {
2665
+ return function(e) {
2666
+ return _this._on_click(e);
2667
+ };
2668
+ })(this));
2669
+ this.render();
2670
+ }
2671
+
2672
+ Item.prototype._on_click = function(e) {
2673
+ var crumbs, id, title;
2674
+ if (this.$el.hasClass('active')) {
2675
+ e.preventDefault();
2676
+ return;
2677
+ }
2678
+ window._skipHashchange = true;
2679
+ location.hash = $(e.currentTarget).attr('href');
2680
+ title = $(e.currentTarget).attr('data-title');
2681
+ id = $(e.currentTarget).attr('data-id');
2682
+ crumbs = location.href.split('/');
2683
+ if (crumbs[crumbs.length - 2] === 'view') {
2684
+ return this.module.showViewByObjectId(id, this.config, title, true);
2685
+ }
2686
+ if (this.config.objectStore) {
2687
+ return this.module.showViewByObjectId('', this.config, title, true);
2688
+ }
2689
+ return this.module.showNestedList(_last(crumbs), true);
2690
+ };
2691
+
2692
+ Item.prototype._is_folder = function() {
2659
2693
  if (this.object._title) {
2660
2694
  return true;
2661
2695
  } else {
@@ -2663,7 +2697,7 @@ this.Item = (function() {
2663
2697
  }
2664
2698
  };
2665
2699
 
2666
- Item.prototype._renderTitle = function() {
2700
+ Item.prototype._render_title = function() {
2667
2701
  var title;
2668
2702
  title = this.object._title;
2669
2703
  if (title == null) {
@@ -2681,7 +2715,7 @@ this.Item = (function() {
2681
2715
  return this.$el.attr('data-title', title);
2682
2716
  };
2683
2717
 
2684
- Item.prototype._renderSubtitle = function() {
2718
+ Item.prototype._render_subtitle = function() {
2685
2719
  var subtitle;
2686
2720
  if (this.config.itemSubtitleField) {
2687
2721
  subtitle = this.object[this.config.itemSubtitleField];
@@ -2693,7 +2727,7 @@ this.Item = (function() {
2693
2727
  }
2694
2728
  };
2695
2729
 
2696
- Item.prototype._renderThumbnail = function() {
2730
+ Item.prototype._render_thumbnail = function() {
2697
2731
  var imageUrl;
2698
2732
  if (this.config.itemThumbnail) {
2699
2733
  imageUrl = this.config.itemThumbnail(this.object);
@@ -2707,13 +2741,13 @@ this.Item = (function() {
2707
2741
 
2708
2742
  Item.prototype.render = function() {
2709
2743
  this.$el.html('').removeClass('item-folder has-subtitle has-thumbnail');
2710
- this._renderTitle();
2711
- if (this._isFolder()) {
2744
+ this._render_title();
2745
+ if (this._is_folder()) {
2712
2746
  this.$el.addClass('item-folder');
2713
2747
  return this.$el.append($("<div class='icon-folder'></div>"));
2714
2748
  } else {
2715
- this._renderSubtitle();
2716
- this._renderThumbnail();
2749
+ this._render_subtitle();
2750
+ this._render_thumbnail();
2717
2751
  if (this.config.arrayStore && this.config.arrayStore.reorderable) {
2718
2752
  this.$el.addClass('reorderable');
2719
2753
  return this.$el.append($("<div class='icon-reorder'></div>"));
@@ -2721,39 +2755,6 @@ this.Item = (function() {
2721
2755
  }
2722
2756
  };
2723
2757
 
2724
- function Item(module, path, object1, config) {
2725
- this.module = module;
2726
- this.path = path;
2727
- this.object = object1;
2728
- this.config = config;
2729
- this.$el = $("<a class='item silent' href='" + this.path + "' data-id='" + this.object._id + "' data-title=''></a>");
2730
- this.$el.on('click', (function(_this) {
2731
- return function(e) {
2732
- return _this.onClick(e);
2733
- };
2734
- })(this));
2735
- this.render();
2736
- }
2737
-
2738
- Item.prototype.onClick = function(e) {
2739
- var crumbs, id, object, title;
2740
- window._skipHashchange = true;
2741
- location.hash = $(e.currentTarget).attr('href');
2742
- title = $(e.currentTarget).attr('data-title');
2743
- id = $(e.currentTarget).attr('data-id');
2744
- crumbs = location.href.split('/');
2745
- if (this.config.arrayStore && crumbs[crumbs.length - 2] === 'view') {
2746
- object = this.config.arrayStore.get(id);
2747
- }
2748
- if (this.config.objectStore) {
2749
- object = this.config.objectStore.get();
2750
- }
2751
- if (object) {
2752
- return this.module.showView(object, this.config, title, true);
2753
- }
2754
- return this.module.showNestedList(_last(crumbs), true);
2755
- };
2756
-
2757
2758
  Item.prototype.destroy = function() {
2758
2759
  return this.$el.remove();
2759
2760
  };
@@ -2769,9 +2770,95 @@ this.Item = (function() {
2769
2770
  })();
2770
2771
 
2771
2772
  this.List = (function() {
2772
- List.prototype._loading = function(callback) {
2773
- this.$el.addClass('list-loading');
2774
- return callback();
2773
+ function List(module, name, config1, parentList) {
2774
+ var base, ref, ref1, ref2;
2775
+ this.module = module;
2776
+ this.name = name;
2777
+ this.config = config1;
2778
+ this.parentList = parentList;
2779
+ this.configItemsCount = 0;
2780
+ this.path = this._path();
2781
+ this.items = {};
2782
+ this.title = (ref = this.config.title) != null ? ref : this.name.titleize();
2783
+ this.itemClass = (ref1 = this.config.itemClass) != null ? ref1 : Item;
2784
+ this.showWithParent = false;
2785
+ if (this.parentList) {
2786
+ this.showWithParent = this.parentList.config.showNestedListsAside || false;
2787
+ }
2788
+ if ((ref2 = this.config.showListWithParent) != null) {
2789
+ ref2;
2790
+ } else {
2791
+ false;
2792
+ };
2793
+ this.$el = $("<div class='list " + this.name + "'>");
2794
+ this.module.$el.append(this.$el);
2795
+ if (this.parentList) {
2796
+ this.$el.hide();
2797
+ }
2798
+ this.$items = $("<div class='items'>");
2799
+ this.$el.append(this.$items);
2800
+ this.$header = $("<header></header>");
2801
+ this.$el.append(this.$header);
2802
+ if (this.parentList) {
2803
+ this.$backBtn = $("<a href='#/" + this.parentList.path + "' class='back silent'></a>");
2804
+ this.$backBtn.on('click', (function(_this) {
2805
+ return function(e) {
2806
+ return _this._on_back(e);
2807
+ };
2808
+ })(this));
2809
+ } else {
2810
+ this.$backBtn = $("<a href='#/' class='back'></a>");
2811
+ }
2812
+ this.$header.prepend(this.$backBtn);
2813
+ this.$header.append("<div class='spinner'></div>");
2814
+ this.$header.append("<span class='title'>" + this.title + "</span>");
2815
+ if (!this.config.disableNewItems && this.config.formSchema) {
2816
+ this.$newBtn = $("<a href='#/" + this.path + "/new' class='new silent'></a>");
2817
+ this.$newBtn.on('click', (function(_this) {
2818
+ return function(e) {
2819
+ return _this._on_new(e);
2820
+ };
2821
+ })(this));
2822
+ this.$header.append(this.$newBtn);
2823
+ }
2824
+ this.$search = $("<div class='search' style='display: none;'>\n <a href='#' class='icon'></a>\n <input type='text' placeholder='Search...' />\n <a href='#' class='cancel'>Cancel</a>\n</div>");
2825
+ this.$header.append(this.$search);
2826
+ if (this.config.items) {
2827
+ this._process_config_items();
2828
+ }
2829
+ if (this.config.arrayStore) {
2830
+ this._bind_config_array_store();
2831
+ }
2832
+ if (this.config.objectStore) {
2833
+ this._bind_config_object_store();
2834
+ }
2835
+ this._bind_hashchange();
2836
+ if (typeof (base = this.config).onListInit === "function") {
2837
+ base.onListInit(this);
2838
+ }
2839
+ }
2840
+
2841
+ List.prototype._bind_hashchange = function() {
2842
+ return $(chr).on('hashchange', (function(_this) {
2843
+ return function() {
2844
+ return _this._set_active_item();
2845
+ };
2846
+ })(this));
2847
+ };
2848
+
2849
+ List.prototype._set_active_item = function() {
2850
+ var a, hash, i, itemPath, len, ref;
2851
+ hash = window.location.hash;
2852
+ if (hash.startsWith("#/" + this.module.name)) {
2853
+ ref = this.$items.children();
2854
+ for (i = 0, len = ref.length; i < len; i++) {
2855
+ a = ref[i];
2856
+ itemPath = $(a).attr('href');
2857
+ if (hash.startsWith(itemPath)) {
2858
+ return $(a).addClass('active');
2859
+ }
2860
+ }
2861
+ }
2775
2862
  };
2776
2863
 
2777
2864
  List.prototype._path = function() {
@@ -2785,24 +2872,7 @@ this.List = (function() {
2785
2872
  return this.module.name + (crumbs.length > 0 ? '/' + crumbs.reverse().join('/') : '');
2786
2873
  };
2787
2874
 
2788
- List.prototype._updateItemPosition = function(item, position) {
2789
- position = this.configItemsCount + position;
2790
- if (position === 0) {
2791
- return this.$items.prepend(item.$el);
2792
- } else {
2793
- this.$items.append(item.$el.hide());
2794
- return $(this.$items.children()[position - 1]).after(item.$el.show());
2795
- }
2796
- };
2797
-
2798
- List.prototype._addItem = function(path, object, position, config) {
2799
- var item;
2800
- item = new Item(this.module, path, object, config);
2801
- this.items[object._id] = item;
2802
- return this._updateItemPosition(item, position);
2803
- };
2804
-
2805
- List.prototype._processConfigItems = function() {
2875
+ List.prototype._process_config_items = function() {
2806
2876
  var config, object, ref, ref1, results, slug;
2807
2877
  ref = this.config.items;
2808
2878
  results = [];
@@ -2812,49 +2882,54 @@ this.List = (function() {
2812
2882
  _id: slug,
2813
2883
  _title: (ref1 = config.title) != null ? ref1 : slug.titleize()
2814
2884
  };
2815
- if (config.objectStore) {
2816
- $.extend(object, config.objectStore.get());
2817
- }
2818
2885
  if (config.items || config.arrayStore) {
2819
2886
  this.module.addNestedList(slug, config, this);
2820
2887
  }
2821
- this._addItem("#/" + this.path + "/" + slug, object, 0, config);
2888
+ this._add_item("#/" + this.path + "/" + slug, object, 0, config);
2822
2889
  results.push(this.configItemsCount += 1);
2823
2890
  }
2824
2891
  return results;
2825
2892
  };
2826
2893
 
2827
- List.prototype._bindConfigObjectStore = function() {};
2894
+ List.prototype._bind_config_object_store = function() {};
2828
2895
 
2829
- List.prototype._bindConfigArrayStore = function() {
2896
+ List.prototype._bind_config_array_store = function() {
2830
2897
  this.config.arrayStore.on('object_added', (function(_this) {
2831
2898
  return function(e, data) {
2832
- _this._addItem("#/" + _this.path + "/view/" + data.object._id, data.object, data.position, _this.config);
2833
- return typeof data.callback === "function" ? data.callback(data.object) : void 0;
2899
+ return _this._add_item("#/" + _this.path + "/view/" + data.object._id, data.object, data.position, _this.config);
2834
2900
  };
2835
2901
  })(this));
2902
+ if (this.config.objects) {
2903
+ this.config.arrayStore.addObjects(this.config.objects);
2904
+ }
2836
2905
  this.config.arrayStore.on('object_changed', (function(_this) {
2837
2906
  return function(e, data) {
2838
2907
  var item;
2839
2908
  item = _this.items[data.object._id];
2840
- item.render();
2841
- _this._updateItemPosition(item, data.position);
2842
- return typeof data.callback === "function" ? data.callback(data.object) : void 0;
2909
+ if (item) {
2910
+ item.render();
2911
+ return _this._update_item_position(item, data.position);
2912
+ }
2843
2913
  };
2844
2914
  })(this));
2845
2915
  this.config.arrayStore.on('object_removed', (function(_this) {
2846
2916
  return function(e, data) {
2847
- _this.items[data.object_id].destroy();
2848
- return delete _this.items[data.object_id];
2917
+ var item;
2918
+ item = _this.items[data.object_id];
2919
+ if (item) {
2920
+ item.destroy();
2921
+ return delete _this.items[data.object_id];
2922
+ }
2849
2923
  };
2850
2924
  })(this));
2851
2925
  this.config.arrayStore.on('objects_added', (function(_this) {
2852
2926
  return function(e, data) {
2853
- return _this.$el.removeClass('list-loading');
2927
+ _this._hide_spinner();
2928
+ return _this._set_active_item();
2854
2929
  };
2855
2930
  })(this));
2856
2931
  if (this.config.arrayStore.pagination) {
2857
- _listBindScroll(this);
2932
+ _listBindPagination(this);
2858
2933
  }
2859
2934
  if (this.config.arrayStore.searchable) {
2860
2935
  _listBindSearch(this);
@@ -2864,108 +2939,95 @@ this.List = (function() {
2864
2939
  }
2865
2940
  };
2866
2941
 
2867
- function List(module, name, config1, parentList) {
2868
- var base, ref;
2869
- this.module = module;
2870
- this.name = name;
2871
- this.config = config1;
2872
- this.parentList = parentList;
2873
- this.configItemsCount = 0;
2874
- this.path = this._path();
2875
- this.items = {};
2876
- this.title = (ref = this.config.title) != null ? ref : this.name.titleize();
2877
- this.$el = $("<div class='list " + this.name + "'>");
2878
- this.module.$el.append(this.$el);
2879
- if (this.parentList) {
2880
- this.$el.hide();
2881
- }
2882
- this.$items = $("<div class='items'>");
2883
- this.$el.append(this.$items);
2884
- this.$header = $("<header></header>");
2885
- if (this.parentList) {
2886
- this.parentListPath = this.parentList.path;
2887
- this.$backBtn = $("<a href='#/" + this.parentListPath + "' class='back silent'></a>");
2888
- this.$backBtn.on('click', (function(_this) {
2889
- return function(e) {
2890
- return _this.onBack(e);
2891
- };
2892
- })(this));
2893
- this.$header.prepend(this.$backBtn);
2942
+ List.prototype._add_item = function(path, object, position, config) {
2943
+ var item;
2944
+ item = new this.itemClass(this.module, path, object, config);
2945
+ this.items[object._id] = item;
2946
+ return this._update_item_position(item, position);
2947
+ };
2948
+
2949
+ List.prototype._update_item_position = function(item, position) {
2950
+ position = this.configItemsCount + position;
2951
+ if (position === 0) {
2952
+ return this.$items.prepend(item.$el);
2894
2953
  } else {
2895
- this.$backBtn = $("<a href='#/' class='back'></a>");
2896
- this.$header.prepend(this.$backBtn);
2897
- }
2898
- this.$header.append("<span class='title'>" + this.title + "</span>");
2899
- if (!this.config.disableNewItems && this.config.formSchema) {
2900
- this.$newBtn = $("<a href='#/" + this.path + "/new' class='new silent'></a>");
2901
- this.$newBtn.on('click', (function(_this) {
2902
- return function(e) {
2903
- return _this.onNew(e);
2904
- };
2905
- })(this));
2906
- this.$header.append(this.$newBtn);
2907
- }
2908
- this.$search = $("<div class='search' style='display: none;'>\n <a href='#' class='icon'></a>\n <input type='text' placeholder='Search...' />\n <a href='#' class='cancel'>Cancel</a>\n</div>");
2909
- this.$header.append(this.$search);
2910
- this.$el.append(this.$header);
2911
- if (this.config.items) {
2912
- this._processConfigItems();
2913
- }
2914
- if (this.config.arrayStore) {
2915
- this._bindConfigArrayStore();
2916
- }
2917
- if (this.config.objectStore) {
2918
- this._bindConfigObjectStore();
2919
- }
2920
- if (typeof (base = this.config).onListInit === "function") {
2921
- base.onListInit(this);
2954
+ this.$items.append(item.$el.hide());
2955
+ return $(this.$items.children()[position - 1]).after(item.$el.show());
2922
2956
  }
2923
- }
2924
-
2925
- List.prototype.selectItem = function(href) {
2926
- return this.$items.children("a[href='" + href + "']").addClass('active');
2927
2957
  };
2928
2958
 
2929
- List.prototype.unselectItems = function() {
2930
- return this.$items.children().removeClass('active');
2959
+ List.prototype._show_spinner = function() {
2960
+ return this.$el.addClass('show-spinner');
2931
2961
  };
2932
2962
 
2933
- List.prototype.hide = function() {
2934
- this.unselectItems();
2935
- return this.$el.hide();
2963
+ List.prototype._hide_spinner = function() {
2964
+ return this.$el.removeClass('show-spinner');
2936
2965
  };
2937
2966
 
2938
- List.prototype.show = function(animate) {
2939
- if (animate == null) {
2940
- animate = false;
2941
- }
2942
- if (animate) {
2943
- return this.$el.fadeIn();
2967
+ List.prototype._on_back = function(e) {
2968
+ this.module.chr.unsetActiveListItems();
2969
+ this.module.destroyView();
2970
+ if (this.showWithParent) {
2971
+ return this.hide(true);
2944
2972
  } else {
2945
- return this.$el.show();
2973
+ return this.module.hideActiveList(true);
2946
2974
  }
2947
2975
  };
2948
2976
 
2949
- List.prototype.onBack = function(e) {
2950
- this.unselectItems();
2951
- this.module.hideActiveList(true);
2952
- return this.module.destroyView();
2953
- };
2954
-
2955
- List.prototype.onNew = function(e) {
2977
+ List.prototype._on_new = function(e) {
2956
2978
  window._skipHashchange = true;
2957
2979
  location.hash = $(e.currentTarget).attr('href');
2958
2980
  return this.module.showView(null, this.config, 'New', true);
2959
2981
  };
2960
2982
 
2961
- List.prototype.updateItems = function(callback) {
2962
- if (!this.config.disableReset) {
2983
+ List.prototype.hide = function(animate) {
2984
+ if (animate) {
2985
+ return this.$el.fadeOut();
2986
+ } else {
2987
+ return this.$el.hide();
2988
+ }
2989
+ };
2990
+
2991
+ List.prototype.show = function(animate, callback) {
2992
+ var onShow;
2993
+ if (animate == null) {
2994
+ animate = false;
2995
+ }
2996
+ onShow = (function(_this) {
2997
+ return function() {
2998
+ var base;
2999
+ _this.$items.scrollTop(0);
3000
+ if (typeof (base = _this.config).onListShow === "function") {
3001
+ base.onListShow(_this);
3002
+ }
3003
+ return typeof callback === "function" ? callback() : void 0;
3004
+ };
3005
+ })(this);
3006
+ if (animate) {
3007
+ this.$el.css({
3008
+ 'z-index': 1,
3009
+ 'box-shadow': 'none'
3010
+ });
3011
+ return this.$el.fadeIn($.fx.speeds._default, (function(_this) {
3012
+ return function() {
3013
+ _this.$el.css({
3014
+ 'z-index': '',
3015
+ 'box-shadow': ''
3016
+ });
3017
+ return onShow();
3018
+ };
3019
+ })(this));
3020
+ } else {
3021
+ this.$el.show();
3022
+ return onShow();
3023
+ }
3024
+ };
3025
+
3026
+ List.prototype.updateItems = function() {
3027
+ if (!this.config.disableUpdateItems) {
2963
3028
  if (this.config.arrayStore) {
2964
- return this._loading((function(_this) {
2965
- return function() {
2966
- return _this.config.arrayStore.reset(callback);
2967
- };
2968
- })(this));
3029
+ this._show_spinner();
3030
+ return this.config.arrayStore.reset();
2969
3031
  }
2970
3032
  }
2971
3033
  };
@@ -2979,60 +3041,46 @@ this.List = (function() {
2979
3041
  })();
2980
3042
 
2981
3043
  this._listBindSearch = function(listEl) {
2982
- var $input, arrayStore;
3044
+ var $input, arrayStore, cancelSearch, runSearch, showSearch;
2983
3045
  $input = listEl.$search;
2984
3046
  arrayStore = listEl.config.arrayStore;
3047
+ runSearch = function(input) {
3048
+ var query;
3049
+ query = $(input).val();
3050
+ listEl._show_spinner();
3051
+ return arrayStore.search(query);
3052
+ };
3053
+ showSearch = function() {
3054
+ listEl.$el.addClass('list-search');
3055
+ return $input.find('input').focus();
3056
+ };
3057
+ cancelSearch = function() {
3058
+ listEl.$el.removeClass('list-search');
3059
+ $input.find('input').val('');
3060
+ listEl._show_spinner();
3061
+ return arrayStore.reset();
3062
+ };
2985
3063
  $input.show();
2986
- $input.on('keydown', 'input', (function(_this) {
3064
+ $input.on('keyup', 'input', (function(_this) {
2987
3065
  return function(e) {
2988
- var query;
3066
+ if (e.keyCode === 27) {
3067
+ return cancelSearch();
3068
+ }
2989
3069
  if (e.keyCode === 13) {
2990
- query = $(e.target).val();
2991
- return listEl._loading(function() {
2992
- return arrayStore.search(query);
2993
- });
3070
+ return runSearch(e.target);
2994
3071
  }
2995
3072
  };
2996
3073
  })(this));
2997
3074
  $input.on('click', '.icon', (function(_this) {
2998
3075
  return function(e) {
2999
3076
  e.preventDefault();
3000
- listEl.$el.addClass('list-search');
3001
- return $input.find('input').focus();
3077
+ return showSearch();
3002
3078
  };
3003
3079
  })(this));
3004
3080
  return $input.on('click', '.cancel', (function(_this) {
3005
3081
  return function(e) {
3006
3082
  e.preventDefault();
3007
- listEl.$el.removeClass('list-search');
3008
- $input.find('input').val('');
3009
- return listEl._loading(function() {
3010
- return arrayStore.reset();
3011
- });
3012
- };
3013
- })(this));
3014
- };
3015
-
3016
- this._listBindScroll = function(listEl) {
3017
- var $container, $list, arrayStore;
3018
- $container = listEl.$el;
3019
- $list = listEl.$items;
3020
- arrayStore = listEl.config.arrayStore;
3021
- return $list.scroll((function(_this) {
3022
- return function(e) {
3023
- var $listChildren, listChildrenCount, listFirstChildHeight, listHeight, viewHeight;
3024
- if (!arrayStore.dataFetchLock) {
3025
- $listChildren = $list.children();
3026
- listChildrenCount = $listChildren.length;
3027
- listFirstChildHeight = $listChildren.first().outerHeight();
3028
- listHeight = listChildrenCount * listFirstChildHeight;
3029
- viewHeight = $container.height();
3030
- if (listHeight < (viewHeight + e.target.scrollTop + 100)) {
3031
- return listEl._loading(function() {
3032
- return arrayStore.load();
3033
- });
3034
- }
3035
- }
3083
+ return cancelSearch();
3036
3084
  };
3037
3085
  })(this));
3038
3086
  };
@@ -3096,25 +3144,48 @@ this._listBindReorder = function(listEl) {
3096
3144
  };
3097
3145
 
3098
3146
  this.View = (function() {
3099
- View.prototype._renderForm = function() {
3147
+ function View(module, config, closePath, object1, title1) {
3100
3148
  var ref;
3101
- if ((ref = this.form) != null) {
3102
- ref.destroy();
3149
+ this.module = module;
3150
+ this.config = config;
3151
+ this.closePath = closePath;
3152
+ this.object = object1;
3153
+ this.title = title1;
3154
+ this.store = (ref = this.config.arrayStore) != null ? ref : this.config.objectStore;
3155
+ this.$el = $("<section class='view " + this.module.name + "'>");
3156
+ this.$el.hide();
3157
+ if (this.config.fullsizeView) {
3158
+ this.$el.addClass('fullsize');
3103
3159
  }
3104
- this.form = new Form(this.object, this.config);
3105
- if (!(this.config.disableDelete || this.config.objectStore || this._isNew())) {
3106
- this.$deleteBtn = $("<a href='#' class='delete'>Delete</a>");
3107
- this.$deleteBtn.on('click', (function(_this) {
3160
+ this.$header = $("<header></header>");
3161
+ this.$el.append(this.$header);
3162
+ this.$title = $("<div class='title'></div>");
3163
+ this.$header.append(this.$title);
3164
+ this.$closeBtn = $("<a href='#/" + this.closePath + "' class='close silent'>Close</a>");
3165
+ this.$closeBtn.on('click', (function(_this) {
3166
+ return function(e) {
3167
+ return _this._on_close(e);
3168
+ };
3169
+ })(this));
3170
+ this.$header.append(this.$closeBtn);
3171
+ if (!this.config.disableSave) {
3172
+ this.$saveBtn = $("<a href='#' class='save'>Save</a>");
3173
+ this.$saveBtn.on('click', (function(_this) {
3108
3174
  return function(e) {
3109
- return _this.onDelete(e);
3175
+ return _this._on_save(e);
3110
3176
  };
3111
3177
  })(this));
3112
- this.form.$el.append(this.$deleteBtn);
3178
+ this.$header.append(this.$saveBtn);
3113
3179
  }
3114
- return this.$el.append(this.form.$el);
3115
- };
3180
+ this._render();
3181
+ }
3116
3182
 
3117
3183
  View.prototype._render = function() {
3184
+ this._update_title();
3185
+ return this._render_form();
3186
+ };
3187
+
3188
+ View.prototype._update_title = function() {
3118
3189
  var title, titleText;
3119
3190
  title = this.title;
3120
3191
  if (this.config.itemTitleField) {
@@ -3129,47 +3200,55 @@ this.View = (function() {
3129
3200
  title = "No Title";
3130
3201
  }
3131
3202
  titleText = $("<div>" + title + "</div>").text();
3132
- this.$title.html(titleText);
3133
- return this._renderForm();
3203
+ return this.$title.html(titleText);
3134
3204
  };
3135
3205
 
3136
- View.prototype._initializeFormPlugins = function() {
3137
- var base;
3138
- this.form.initializePlugins();
3139
- return typeof (base = this.config).onViewShow === "function" ? base.onViewShow(this) : void 0;
3206
+ View.prototype._render_form = function() {
3207
+ var ref;
3208
+ if ((ref = this.form) != null) {
3209
+ ref.destroy();
3210
+ }
3211
+ this.form = new Form(this.object, this.config);
3212
+ if (!(this.config.disableDelete || this.config.objectStore || this._is_new())) {
3213
+ this.$deleteBtn = $("<a href='#' class='delete'>Delete</a>");
3214
+ this.$deleteBtn.on('click', (function(_this) {
3215
+ return function(e) {
3216
+ return _this._on_delete(e);
3217
+ };
3218
+ })(this));
3219
+ this.form.$el.append(this.$deleteBtn);
3220
+ }
3221
+ return this.$el.append(this.form.$el);
3140
3222
  };
3141
3223
 
3142
- View.prototype._updateObject = function(value) {
3143
- this.$el.addClass('view-saving');
3224
+ View.prototype._update_object = function(value) {
3225
+ this._start_saving();
3144
3226
  return this.store.update(this.object._id, value, {
3145
3227
  onSuccess: (function(_this) {
3146
- return function(object) {
3228
+ return function(object1) {
3147
3229
  var formScrollPosition;
3230
+ _this.object = object1;
3148
3231
  if (_this.config.arrayStore) {
3149
3232
  _this.title = null;
3150
3233
  }
3151
3234
  formScrollPosition = _this.form.$el.scrollTop();
3152
3235
  _this._render();
3153
- _this._initializeFormPlugins();
3236
+ _this._initialize_form_plugins();
3154
3237
  _this.form.$el.scrollTop(formScrollPosition);
3155
- return setTimeout((function() {
3156
- return _this.$el.removeClass('view-saving');
3157
- }), 250);
3238
+ return _this._stop_saving();
3158
3239
  };
3159
3240
  })(this),
3160
3241
  onError: (function(_this) {
3161
3242
  return function(errors) {
3162
- _this.validationErrors(errors);
3163
- return setTimeout((function() {
3164
- return _this.$el.removeClass('view-saving');
3165
- }), 250);
3243
+ _this._validation_errors('Changes were not saved.', errors);
3244
+ return _this._stop_saving();
3166
3245
  };
3167
3246
  })(this)
3168
3247
  });
3169
3248
  };
3170
3249
 
3171
- View.prototype._createObject = function(value) {
3172
- this.$el.addClass('view-saving');
3250
+ View.prototype._create_object = function(value) {
3251
+ this._start_saving();
3173
3252
  return this.store.push(value, {
3174
3253
  onSuccess: (function(_this) {
3175
3254
  return function(object) {
@@ -3178,80 +3257,41 @@ this.View = (function() {
3178
3257
  })(this),
3179
3258
  onError: (function(_this) {
3180
3259
  return function(errors) {
3181
- _this.validationErrors(errors);
3182
- return setTimeout((function() {
3183
- return _this.$el.removeClass('view-saving');
3184
- }), 250);
3260
+ _this._validation_errors('Item were not created.', errors);
3261
+ return _this._stop_saving();
3185
3262
  };
3186
3263
  })(this)
3187
3264
  });
3188
3265
  };
3189
3266
 
3190
- View.prototype._isNew = function() {
3191
- return !this.object;
3192
- };
3193
-
3194
- function View(module, config, closePath, object1, title1) {
3195
- var ref;
3196
- this.module = module;
3197
- this.config = config;
3198
- this.closePath = closePath;
3199
- this.object = object1;
3200
- this.title = title1;
3201
- this.store = (ref = this.config.arrayStore) != null ? ref : this.config.objectStore;
3202
- this.$el = $("<section class='view " + this.module.name + "'>");
3203
- this.$el.hide();
3204
- this.$header = $("<header></header>");
3205
- this.$title = $("<div class='title'></div>");
3206
- this.$header.append(this.$title);
3207
- this.$closeBtn = $("<a href='#/" + this.closePath + "' class='close silent'>Close</a>");
3208
- this.$closeBtn.on('click', (function(_this) {
3209
- return function(e) {
3210
- return _this.onClose(e);
3211
- };
3212
- })(this));
3213
- this.$header.append(this.$closeBtn);
3214
- if (!this.config.disableSave) {
3215
- this.$saveBtn = $("<a href='#' class='save'>Save</a>");
3216
- this.$saveBtn.on('click', (function(_this) {
3217
- return function(e) {
3218
- return _this.onSave(e);
3219
- };
3220
- })(this));
3221
- this.$header.append(this.$saveBtn);
3222
- }
3223
- this.$el.append(this.$header);
3224
- this._render();
3225
- }
3226
-
3227
- View.prototype.show = function(animate, callback) {
3228
- if (animate) {
3229
- return this.$el.fadeIn($.fx.speeds._default, (function(_this) {
3230
- return function() {
3231
- _this._initializeFormPlugins();
3232
- return typeof callback === "function" ? callback() : void 0;
3233
- };
3234
- })(this));
3235
- } else {
3236
- return this.$el.show(0, (function(_this) {
3237
- return function() {
3238
- _this._initializeFormPlugins();
3239
- return typeof callback === "function" ? callback() : void 0;
3240
- };
3241
- })(this));
3242
- }
3267
+ View.prototype._start_saving = function() {
3268
+ return this.$el.addClass('view-saving');
3243
3269
  };
3244
3270
 
3245
- View.prototype.destroy = function() {
3246
- var ref;
3247
- if ((ref = this.form) != null) {
3248
- ref.destroy();
3249
- }
3250
- return this.$el.remove();
3271
+ View.prototype._stop_saving = function() {
3272
+ return setTimeout(((function(_this) {
3273
+ return function() {
3274
+ return _this.$el.removeClass('view-saving');
3275
+ };
3276
+ })(this)), 250);
3277
+ };
3278
+
3279
+ View.prototype._initialize_form_plugins = function() {
3280
+ var base;
3281
+ this.form.initializePlugins();
3282
+ return typeof (base = this.config).onViewShow === "function" ? base.onViewShow(this) : void 0;
3283
+ };
3284
+
3285
+ View.prototype._validation_errors = function(message, errors) {
3286
+ chr.showError(message);
3287
+ return this.form.showValidationErrors(errors);
3251
3288
  };
3252
3289
 
3253
- View.prototype.onClose = function(e) {
3254
- this.module.unselectActiveListItem();
3290
+ View.prototype._is_new = function() {
3291
+ return !this.object;
3292
+ };
3293
+
3294
+ View.prototype._on_close = function(e) {
3255
3295
  return this.$el.fadeOut($.fx.speeds._default, (function(_this) {
3256
3296
  return function() {
3257
3297
  return _this.destroy();
@@ -3259,18 +3299,18 @@ this.View = (function() {
3259
3299
  })(this));
3260
3300
  };
3261
3301
 
3262
- View.prototype.onSave = function(e) {
3302
+ View.prototype._on_save = function(e) {
3263
3303
  var serializedFormObj;
3264
3304
  e.preventDefault();
3265
3305
  serializedFormObj = this.form.serialize();
3266
3306
  if (this.object) {
3267
- return this._updateObject(serializedFormObj);
3307
+ return this._update_object(serializedFormObj);
3268
3308
  } else {
3269
- return this._createObject(serializedFormObj);
3309
+ return this._create_object(serializedFormObj);
3270
3310
  }
3271
3311
  };
3272
3312
 
3273
- View.prototype.onDelete = function(e) {
3313
+ View.prototype._on_delete = function(e) {
3274
3314
  e.preventDefault();
3275
3315
  if (confirm("Are you sure?")) {
3276
3316
  this.store.remove(this.object._id);
@@ -3284,8 +3324,30 @@ this.View = (function() {
3284
3324
  }
3285
3325
  };
3286
3326
 
3287
- View.prototype.validationErrors = function(errors) {
3288
- return this.form.showValidationErrors(errors);
3327
+ View.prototype.show = function(animate, callback) {
3328
+ if (animate) {
3329
+ return this.$el.fadeIn($.fx.speeds._default, (function(_this) {
3330
+ return function() {
3331
+ _this._initialize_form_plugins();
3332
+ return typeof callback === "function" ? callback() : void 0;
3333
+ };
3334
+ })(this));
3335
+ } else {
3336
+ return this.$el.show(0, (function(_this) {
3337
+ return function() {
3338
+ _this._initialize_form_plugins();
3339
+ return typeof callback === "function" ? callback() : void 0;
3340
+ };
3341
+ })(this));
3342
+ }
3343
+ };
3344
+
3345
+ View.prototype.destroy = function() {
3346
+ var ref;
3347
+ if ((ref = this.form) != null) {
3348
+ ref.destroy();
3349
+ }
3350
+ return this.$el.remove();
3289
3351
  };
3290
3352
 
3291
3353
  return View;
@@ -3293,40 +3355,47 @@ this.View = (function() {
3293
3355
  })();
3294
3356
 
3295
3357
  this.Module = (function() {
3296
- function Module(chr, name, config1) {
3297
- var menuTitle, ref;
3298
- this.chr = chr;
3358
+ function Module(chr1, name, config1) {
3359
+ var base, firstNestedList, menuPath, menuTitle, ref;
3360
+ this.chr = chr1;
3299
3361
  this.name = name;
3300
3362
  this.config = config1;
3301
3363
  this.nestedLists = {};
3302
3364
  this.$el = $("<section class='module " + this.name + "' style='display: none;'>");
3303
3365
  this.chr.$el.append(this.$el);
3366
+ this.activeList = this.rootList = new List(this, this.name, this.config);
3304
3367
  menuTitle = (ref = this.config.menuTitle) != null ? ref : this.config.title;
3305
3368
  if (menuTitle == null) {
3306
3369
  menuTitle = this.name.titleize();
3307
3370
  }
3308
- this.chr.addMenuItem(this.name, menuTitle);
3309
- this.activeList = this.rootList = new List(this, this.name, this.config);
3371
+ menuPath = this.name;
3372
+ if (this.config.showNestedListsAside) {
3373
+ this.$el.addClass('first-list-aside');
3374
+ firstNestedList = _firstNonEmptyValue(this.nestedLists);
3375
+ if (!_isMobile() && firstNestedList) {
3376
+ menuPath += "/" + firstNestedList.name;
3377
+ }
3378
+ }
3379
+ this.chr.addMenuItem(menuPath, menuTitle);
3380
+ if (typeof (base = this.config).onModuleInit === "function") {
3381
+ base.onModuleInit(this);
3382
+ }
3310
3383
  }
3311
3384
 
3312
- Module.prototype._updateActiveListItems = function() {
3385
+ Module.prototype._update_active_list_items = function() {
3313
3386
  if (!this.activeList.isVisible()) {
3314
3387
  return this.activeList.updateItems();
3315
3388
  }
3316
3389
  };
3317
3390
 
3318
- Module.prototype.addNestedList = function(listName, config, parentList) {
3319
- return this.nestedLists[listName] = new List(this, listName, config, parentList);
3320
- };
3321
-
3322
- Module.prototype.selectActiveListItem = function(href) {
3323
- this.unselectActiveListItem();
3324
- return this.activeList.selectItem(href);
3391
+ Module.prototype._view_path = function() {
3392
+ var currentList, ref;
3393
+ currentList = (ref = this.visibleNestedListShownWithParent()) != null ? ref : this.activeList;
3394
+ return currentList.path;
3325
3395
  };
3326
3396
 
3327
- Module.prototype.unselectActiveListItem = function() {
3328
- var ref;
3329
- return (ref = this.activeList) != null ? ref.unselectItems() : void 0;
3397
+ Module.prototype.addNestedList = function(listName, config, parentList) {
3398
+ return this.nestedLists[listName] = new List(this, listName, config, parentList);
3330
3399
  };
3331
3400
 
3332
3401
  Module.prototype.hideActiveList = function(animate) {
@@ -3338,8 +3407,7 @@ this.Module = (function() {
3338
3407
  } else {
3339
3408
  this.activeList.$el.hide();
3340
3409
  }
3341
- this.activeList = this.activeList.parentList;
3342
- return this.unselectActiveListItem();
3410
+ return this.activeList = this.activeList.parentList;
3343
3411
  };
3344
3412
 
3345
3413
  Module.prototype.showView = function(object, config, title, animate) {
@@ -3347,9 +3415,8 @@ this.Module = (function() {
3347
3415
  if (animate == null) {
3348
3416
  animate = false;
3349
3417
  }
3350
- newView = new View(this, config, this.activeList.path, object, title);
3418
+ newView = new View(this, config, this._view_path(), object, title);
3351
3419
  this.chr.$el.append(newView.$el);
3352
- this.selectActiveListItem(location.hash);
3353
3420
  return newView.show(animate, (function(_this) {
3354
3421
  return function() {
3355
3422
  _this.destroyView();
@@ -3358,15 +3425,39 @@ this.Module = (function() {
3358
3425
  })(this));
3359
3426
  };
3360
3427
 
3428
+ Module.prototype.showViewByObjectId = function(objectId, config, title, animate) {
3429
+ var onError, onSuccess;
3430
+ if (animate == null) {
3431
+ animate = false;
3432
+ }
3433
+ onSuccess = (function(_this) {
3434
+ return function(object) {
3435
+ return _this.showView(object, config, title, animate);
3436
+ };
3437
+ })(this);
3438
+ onError = function() {
3439
+ return chr.showError("can\'t show view for requested object");
3440
+ };
3441
+ if (objectId === '') {
3442
+ return config.objectStore.loadObject({
3443
+ onSuccess: onSuccess,
3444
+ onError: onError
3445
+ });
3446
+ } else {
3447
+ return config.arrayStore.loadObject(objectId, {
3448
+ onSuccess: onSuccess,
3449
+ onError: onError
3450
+ });
3451
+ }
3452
+ };
3453
+
3361
3454
  Module.prototype.destroyView = function() {
3362
3455
  var ref;
3363
3456
  return (ref = this.view) != null ? ref.destroy() : void 0;
3364
3457
  };
3365
3458
 
3366
3459
  Module.prototype.show = function() {
3367
- this.chr.selectMenuItem(this.name);
3368
- this.unselectActiveListItem();
3369
- this._updateActiveListItems();
3460
+ this._update_active_list_items();
3370
3461
  this.$el.show();
3371
3462
  return this.activeList.show(false);
3372
3463
  };
@@ -3375,7 +3466,6 @@ this.Module = (function() {
3375
3466
  if (animate == null) {
3376
3467
  animate = false;
3377
3468
  }
3378
- this.unselectActiveListItem();
3379
3469
  this.hideNestedLists();
3380
3470
  if (animate) {
3381
3471
  if (this.view) {
@@ -3393,13 +3483,24 @@ this.Module = (function() {
3393
3483
  };
3394
3484
 
3395
3485
  Module.prototype.showNestedList = function(listName, animate) {
3486
+ var listToShow;
3396
3487
  if (animate == null) {
3397
3488
  animate = false;
3398
3489
  }
3399
- this.selectActiveListItem(location.hash);
3400
- this.activeList = this.nestedLists[listName];
3401
- this._updateActiveListItems();
3402
- this.activeList.show(animate);
3490
+ listToShow = this.nestedLists[listName];
3491
+ if (listToShow.showWithParent) {
3492
+ listToShow.updateItems();
3493
+ listToShow.show(animate, (function(_this) {
3494
+ return function() {
3495
+ var exceptList;
3496
+ return _this.hideNestedLists(exceptList = listName);
3497
+ };
3498
+ })(this));
3499
+ } else {
3500
+ this.activeList = listToShow;
3501
+ this._update_active_list_items();
3502
+ this.activeList.show(animate);
3503
+ }
3403
3504
  if (animate && this.view) {
3404
3505
  return this.view.$el.fadeOut($.fx.speeds._default, (function(_this) {
3405
3506
  return function() {
@@ -3409,33 +3510,29 @@ this.Module = (function() {
3409
3510
  }
3410
3511
  };
3411
3512
 
3412
- Module.prototype.hideNestedLists = function() {
3513
+ Module.prototype.hideNestedLists = function(exceptList) {
3413
3514
  var key, list, ref, results;
3414
3515
  this.activeList = this.rootList;
3415
3516
  ref = this.nestedLists;
3416
3517
  results = [];
3417
3518
  for (key in ref) {
3418
3519
  list = ref[key];
3419
- results.push(list.hide());
3520
+ if (key !== exceptList) {
3521
+ results.push(list.hide());
3522
+ }
3420
3523
  }
3421
3524
  return results;
3422
3525
  };
3423
3526
 
3424
- Module.prototype.showViewWhenObjectsAreReady = function(objectId, config) {
3425
- var object;
3426
- object = config.arrayStore.get(objectId);
3427
- if (object) {
3428
- return this.showView(object, config);
3527
+ Module.prototype.visibleNestedListShownWithParent = function() {
3528
+ var key, list, ref;
3529
+ ref = this.nestedLists;
3530
+ for (key in ref) {
3531
+ list = ref[key];
3532
+ if (list.isVisible() && list.showWithParent) {
3533
+ return list;
3534
+ }
3429
3535
  }
3430
- return $(config.arrayStore).one('objects_added', (function(_this) {
3431
- return function(e, data) {
3432
- object = config.arrayStore.get(objectId);
3433
- if (object) {
3434
- return _this.showView(object, config);
3435
- }
3436
- return console.log("object " + objectId + " is not in the list");
3437
- };
3438
- })(this));
3439
3536
  };
3440
3537
 
3441
3538
  return Module;
@@ -4610,50 +4707,15 @@ this.NestedForm = (function() {
4610
4707
 
4611
4708
  _chrFormInputs['form'] = NestedForm;
4612
4709
 
4613
- this.ObjectStore = (function() {
4614
- function ObjectStore(config) {
4615
- this.config = config != null ? config : {};
4616
- this._initialize_database();
4617
- }
4618
-
4619
- ObjectStore.prototype._update_data_object = function(value, callback) {
4620
- return typeof callback === "function" ? callback($.extend(this._data, value)) : void 0;
4621
- };
4622
-
4623
- ObjectStore.prototype._fetch_data = function() {
4624
- return this._data = this.config.data;
4625
- };
4626
-
4627
- ObjectStore.prototype._initialize_database = function() {
4628
- return this._fetch_data();
4629
- };
4630
-
4631
- ObjectStore.prototype.get = function() {
4632
- return this._data;
4633
- };
4634
-
4635
- ObjectStore.prototype.update = function(id, value, callback) {
4636
- return this._update_data_object(value, callback);
4637
- };
4638
-
4639
- return ObjectStore;
4640
-
4641
- })();
4642
-
4643
4710
  this.ArrayStore = (function() {
4644
4711
  function ArrayStore(config) {
4645
- var ref, ref1, ref2, ref3, ref4;
4712
+ var ref, ref1, ref2;
4646
4713
  this.config = config != null ? config : {};
4647
4714
  this._map = {};
4648
4715
  this._data = [];
4649
4716
  this.sortBy = (ref = this.config.sortBy) != null ? ref : false;
4650
4717
  this.sortReverse = (ref1 = this.config.sortReverse) != null ? ref1 : false;
4651
4718
  this.reorderable = (ref2 = this.config.reorderable) != null ? ref2 : false;
4652
- if (this.sortBy === false) {
4653
- this.newItemOnTop = (ref3 = this.config.newItemOnTop) != null ? ref3 : true;
4654
- } else {
4655
- this.newItemOnTop = (ref4 = this.config.newItemOnTop) != null ? ref4 : false;
4656
- }
4657
4719
  this._initialize_reorderable();
4658
4720
  this._initialize_database();
4659
4721
  }
@@ -4673,18 +4735,6 @@ this.ArrayStore = (function() {
4673
4735
 
4674
4736
  ArrayStore.prototype._initialize_database = function() {};
4675
4737
 
4676
- ArrayStore.prototype._fetch_data = function() {
4677
- var i, len, o, ref;
4678
- if (this.config.data) {
4679
- ref = this.config.data;
4680
- for (i = 0, len = ref.length; i < len; i++) {
4681
- o = ref[i];
4682
- this._add_data_object(o);
4683
- }
4684
- }
4685
- return $(this).trigger('objects_added');
4686
- };
4687
-
4688
4738
  ArrayStore.prototype._sort_data = function() {
4689
4739
  var direction, fieldName, sortByMethod;
4690
4740
  if (this.sortBy) {
@@ -4726,42 +4776,31 @@ this.ArrayStore = (function() {
4726
4776
  return object;
4727
4777
  };
4728
4778
 
4729
- ArrayStore.prototype._add_data_object = function(object, callback) {
4730
- var position;
4731
- object = this._normalize_object_id(object);
4732
- this._map[object._id] = object;
4733
- this._data.push(object);
4734
- this._sort_data();
4735
- position = this._get_data_object_position(object._id);
4736
- return $(this).trigger('object_added', {
4737
- object: object,
4738
- position: position,
4739
- callback: callback
4740
- });
4741
- };
4742
-
4743
- ArrayStore.prototype._add_data_object_on_top = function(object, callback) {
4779
+ ArrayStore.prototype._add_data_object = function(object) {
4744
4780
  var position;
4745
4781
  object = this._normalize_object_id(object);
4746
- this._map[object._id] = object;
4747
- this._data.unshift(object);
4748
- position = 0;
4749
- return $(this).trigger('object_added', {
4750
- object: object,
4751
- position: position,
4752
- callback: callback
4753
- });
4782
+ if (!this._map[object._id]) {
4783
+ this._map[object._id] = object;
4784
+ this._data.push(object);
4785
+ this._sort_data();
4786
+ position = this._get_data_object_position(object._id);
4787
+ return $(this).trigger('object_added', {
4788
+ object: object,
4789
+ position: position
4790
+ });
4791
+ } else {
4792
+ return this._update_data_object(object.id, object);
4793
+ }
4754
4794
  };
4755
4795
 
4756
- ArrayStore.prototype._update_data_object = function(id, value, callback) {
4796
+ ArrayStore.prototype._update_data_object = function(id, value) {
4757
4797
  var object, position;
4758
4798
  object = $.extend(this.get(id), value);
4759
4799
  this._sort_data();
4760
4800
  position = this._get_data_object_position(id);
4761
4801
  return $(this).trigger('object_changed', {
4762
4802
  object: object,
4763
- position: position,
4764
- callback: callback
4803
+ position: position
4765
4804
  });
4766
4805
  };
4767
4806
 
@@ -4769,7 +4808,7 @@ this.ArrayStore = (function() {
4769
4808
  var position;
4770
4809
  position = this._get_data_object_position(id);
4771
4810
  if (position >= 0) {
4772
- delete this._data[position];
4811
+ this._data.splice(position, 1);
4773
4812
  }
4774
4813
  delete this._map[id];
4775
4814
  return $(this).trigger('object_removed', {
@@ -4801,6 +4840,12 @@ this.ArrayStore = (function() {
4801
4840
  return object;
4802
4841
  };
4803
4842
 
4843
+ ArrayStore.prototype.on = function(eventType, callback) {
4844
+ return $(this).on(eventType, function(e, data) {
4845
+ return callback(e, data);
4846
+ });
4847
+ };
4848
+
4804
4849
  ArrayStore.prototype.off = function(eventType) {
4805
4850
  if (eventType) {
4806
4851
  return $(this).off(eventType);
@@ -4809,89 +4854,351 @@ this.ArrayStore = (function() {
4809
4854
  }
4810
4855
  };
4811
4856
 
4812
- ArrayStore.prototype.on = function(eventType, callback) {
4813
- $(this).on(eventType, function(e, data) {
4814
- return callback(e, data);
4857
+ ArrayStore.prototype.get = function(id) {
4858
+ return this._map[id];
4859
+ };
4860
+
4861
+ ArrayStore.prototype.push = function(serializedFormObject, callbacks) {
4862
+ var object;
4863
+ if (callbacks == null) {
4864
+ callbacks = {};
4865
+ }
4866
+ object = this._parse_form_object(serializedFormObject);
4867
+ if (!object._id) {
4868
+ object._id = Date.now();
4869
+ }
4870
+ this._add_data_object(object);
4871
+ return typeof callbacks.onSuccess === "function" ? callbacks.onSuccess() : void 0;
4872
+ };
4873
+
4874
+ ArrayStore.prototype.update = function(id, serializedFormObject, callbacks) {
4875
+ var object;
4876
+ if (callbacks == null) {
4877
+ callbacks = {};
4878
+ }
4879
+ object = this._parse_form_object(serializedFormObject);
4880
+ this._update_data_object(id, object);
4881
+ return typeof callbacks.onSuccess === "function" ? callbacks.onSuccess() : void 0;
4882
+ };
4883
+
4884
+ ArrayStore.prototype.remove = function(id, callbacks) {
4885
+ if (callbacks == null) {
4886
+ callbacks = {};
4887
+ }
4888
+ this._remove_data_object(id);
4889
+ return typeof callbacks.onSuccess === "function" ? callbacks.onSuccess() : void 0;
4890
+ };
4891
+
4892
+ ArrayStore.prototype.reset = function() {
4893
+ return $(this).trigger('objects_added');
4894
+ };
4895
+
4896
+ ArrayStore.prototype.addObjects = function(objects) {
4897
+ var i, len, o;
4898
+ for (i = 0, len = objects.length; i < len; i++) {
4899
+ o = objects[i];
4900
+ this._add_data_object(o);
4901
+ }
4902
+ return $(this).trigger('objects_added');
4903
+ };
4904
+
4905
+ ArrayStore.prototype.data = function() {
4906
+ return this._data;
4907
+ };
4908
+
4909
+ return ArrayStore;
4910
+
4911
+ })();
4912
+
4913
+ this.ObjectStore = (function() {
4914
+ function ObjectStore(config) {
4915
+ this.config = config != null ? config : {};
4916
+ this._initialize_database();
4917
+ }
4918
+
4919
+ ObjectStore.prototype._initialize_database = function() {
4920
+ return this._data = this.config.data;
4921
+ };
4922
+
4923
+ ObjectStore.prototype.loadObject = function() {
4924
+ return this._data;
4925
+ };
4926
+
4927
+ ObjectStore.prototype.update = function(id, value, callback) {
4928
+ $.extend(this._data, value);
4929
+ return typeof callback === "function" ? callback(this._data) : void 0;
4930
+ };
4931
+
4932
+ return ObjectStore;
4933
+
4934
+ })();
4935
+
4936
+ var extend = 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; },
4937
+ hasProp = {}.hasOwnProperty;
4938
+
4939
+ this.RestArrayStore = (function(superClass) {
4940
+ extend(RestArrayStore, superClass);
4941
+
4942
+ function RestArrayStore() {
4943
+ return RestArrayStore.__super__.constructor.apply(this, arguments);
4944
+ }
4945
+
4946
+ RestArrayStore.prototype._initialize_database = function() {
4947
+ this.dataFetchLock = false;
4948
+ return this.ajaxConfig = {};
4949
+ };
4950
+
4951
+ RestArrayStore.prototype._resource_url = function(type, id) {
4952
+ var objectPath;
4953
+ objectPath = id ? "/" + id : '';
4954
+ return "" + this.config.path + objectPath;
4955
+ };
4956
+
4957
+ RestArrayStore.prototype._ajax = function(type, id, data, success, error) {
4958
+ var options;
4959
+ options = $.extend(this.ajaxConfig, {
4960
+ url: this._resource_url(type, id),
4961
+ type: type,
4962
+ data: data,
4963
+ success: (function(_this) {
4964
+ return function(data, textStatus, jqXHR) {
4965
+ if (typeof success === "function") {
4966
+ success(data);
4967
+ }
4968
+ return setTimeout((function() {
4969
+ return _this.dataFetchLock = false;
4970
+ }), 350);
4971
+ };
4972
+ })(this),
4973
+ error: (function(_this) {
4974
+ return function(jqXHR, textStatus, errorThrown) {
4975
+ if (typeof error === "function") {
4976
+ error(jqXHR.responseJSON);
4977
+ }
4978
+ return _this.dataFetchLock = false;
4979
+ };
4980
+ })(this)
4981
+ });
4982
+ this.dataFetchLock = true;
4983
+ return $.ajax(options);
4984
+ };
4985
+
4986
+ RestArrayStore.prototype._sync_with_data_objects = function(objects) {
4987
+ var addObjectIds, dataObjectIds, i, id, j, k, l, len, len1, len2, len3, o, objectIds, objectsMap, removeDataObjectIds, results, updateDataObjectIds;
4988
+ if (objects.length === 0) {
4989
+ return this._reset_data();
4990
+ }
4991
+ if (this._data.length === 0) {
4992
+ return (function() {
4993
+ var i, len, results;
4994
+ results = [];
4995
+ for (i = 0, len = objects.length; i < len; i++) {
4996
+ o = objects[i];
4997
+ results.push(this._add_data_object(o));
4998
+ }
4999
+ return results;
5000
+ }).call(this);
5001
+ }
5002
+ objectsMap = {};
5003
+ for (i = 0, len = objects.length; i < len; i++) {
5004
+ o = objects[i];
5005
+ o = this._normalize_object_id(o);
5006
+ objectsMap[o._id] = o;
5007
+ }
5008
+ objectIds = $.map(objects, function(o) {
5009
+ return o._id;
5010
+ });
5011
+ dataObjectIds = $.map(this._data, function(o) {
5012
+ return o._id;
4815
5013
  });
4816
- if (eventType === 'object_added') {
4817
- return this._fetch_data();
5014
+ addObjectIds = $(objectIds).not(dataObjectIds).get();
5015
+ updateDataObjectIds = $(objectIds).not(addObjectIds).get();
5016
+ removeDataObjectIds = $(dataObjectIds).not(objectIds).get();
5017
+ for (j = 0, len1 = removeDataObjectIds.length; j < len1; j++) {
5018
+ id = removeDataObjectIds[j];
5019
+ this._remove_data_object(id);
5020
+ }
5021
+ for (k = 0, len2 = addObjectIds.length; k < len2; k++) {
5022
+ id = addObjectIds[k];
5023
+ this._add_data_object(objectsMap[id]);
5024
+ }
5025
+ results = [];
5026
+ for (l = 0, len3 = updateDataObjectIds.length; l < len3; l++) {
5027
+ id = updateDataObjectIds[l];
5028
+ results.push(this._update_data_object(id, objectsMap[id]));
4818
5029
  }
5030
+ return results;
4819
5031
  };
4820
5032
 
4821
- ArrayStore.prototype.get = function(id) {
4822
- return this._map[id];
5033
+ RestArrayStore.prototype.loadObject = function(id, callbacks) {
5034
+ if (callbacks == null) {
5035
+ callbacks = {};
5036
+ }
5037
+ if (callbacks.onSuccess == null) {
5038
+ callbacks.onSuccess = $.noop;
5039
+ }
5040
+ if (callbacks.onError == null) {
5041
+ callbacks.onError = $.noop;
5042
+ }
5043
+ return this._ajax('GET', id, {}, ((function(_this) {
5044
+ return function(data) {
5045
+ return callbacks.onSuccess(data);
5046
+ };
5047
+ })(this)), callbacks.onError);
5048
+ };
5049
+
5050
+ RestArrayStore.prototype.load = function(callbacks) {
5051
+ if (callbacks == null) {
5052
+ callbacks = {};
5053
+ }
5054
+ if (callbacks.onSuccess == null) {
5055
+ callbacks.onSuccess = $.noop;
5056
+ }
5057
+ if (callbacks.onError == null) {
5058
+ callbacks.onError = $.noop;
5059
+ }
5060
+ return this._ajax('GET', null, {}, ((function(_this) {
5061
+ return function(data) {
5062
+ var i, len, o;
5063
+ if (data.length > 0) {
5064
+ for (i = 0, len = data.length; i < len; i++) {
5065
+ o = data[i];
5066
+ _this._add_data_object(o);
5067
+ }
5068
+ }
5069
+ callbacks.onSuccess(data);
5070
+ return $(_this).trigger('objects_added', {
5071
+ objects: data
5072
+ });
5073
+ };
5074
+ })(this))(callbacks.onError));
4823
5075
  };
4824
5076
 
4825
- ArrayStore.prototype.push = function(serializedFormObject, callbacks) {
4826
- var object;
4827
- object = this._parse_form_object(serializedFormObject);
4828
- if (!object._id) {
4829
- object._id = Date.now();
5077
+ RestArrayStore.prototype.push = function(serializedFormObject, callbacks) {
5078
+ var obj;
5079
+ if (callbacks == null) {
5080
+ callbacks = {};
4830
5081
  }
4831
- if (this.newItemOnTop) {
4832
- return this._add_data_object_on_top(object, callbacks.onSuccess);
4833
- } else {
4834
- return this._add_data_object(object, callbacks.onSuccess);
5082
+ if (callbacks.onSuccess == null) {
5083
+ callbacks.onSuccess = $.noop;
5084
+ }
5085
+ if (callbacks.onError == null) {
5086
+ callbacks.onError = $.noop;
4835
5087
  }
5088
+ obj = this._parse_form_object(serializedFormObject);
5089
+ return this._ajax('POST', null, obj, ((function(_this) {
5090
+ return function(data) {
5091
+ _this._add_data_object(data);
5092
+ return callbacks.onSuccess(data);
5093
+ };
5094
+ })(this)), callbacks.onError);
4836
5095
  };
4837
5096
 
4838
- ArrayStore.prototype.update = function(id, serializedFormObject, callbacks) {
4839
- var object;
4840
- object = this._parse_form_object(serializedFormObject);
4841
- return this._update_data_object(id, object, callbacks.onSuccess);
5097
+ RestArrayStore.prototype.update = function(id, serializedFormObject, callbacks) {
5098
+ var obj;
5099
+ if (callbacks == null) {
5100
+ callbacks = {};
5101
+ }
5102
+ if (callbacks.onSuccess == null) {
5103
+ callbacks.onSuccess = $.noop;
5104
+ }
5105
+ if (callbacks.onError == null) {
5106
+ callbacks.onError = $.noop;
5107
+ }
5108
+ obj = this._parse_form_object(serializedFormObject);
5109
+ return this._ajax('PUT', id, obj, ((function(_this) {
5110
+ return function(data) {
5111
+ _this._update_data_object(id, data);
5112
+ return callbacks.onSuccess(data);
5113
+ };
5114
+ })(this)), callbacks.onError);
4842
5115
  };
4843
5116
 
4844
- ArrayStore.prototype.remove = function(id) {
4845
- return this._remove_data_object(id);
5117
+ RestArrayStore.prototype.remove = function(id, callbacks) {
5118
+ if (callbacks == null) {
5119
+ callbacks = {};
5120
+ }
5121
+ if (callbacks.onSuccess == null) {
5122
+ callbacks.onSuccess = $.noop;
5123
+ }
5124
+ if (callbacks.onError == null) {
5125
+ callbacks.onError = $.noop;
5126
+ }
5127
+ return this._ajax('DELETE', id, {}, ((function(_this) {
5128
+ return function() {
5129
+ _this._remove_data_object(id);
5130
+ return callbacks.onSuccess();
5131
+ };
5132
+ })(this)), callbacks.onError);
4846
5133
  };
4847
5134
 
4848
- ArrayStore.prototype.reset = function(callback) {
4849
- this._sort_data();
4850
- $(this).trigger('objects_added');
4851
- return typeof callback === "function" ? callback() : void 0;
5135
+ RestArrayStore.prototype.reset = function() {
5136
+ return this._ajax('GET', null, {}, ((function(_this) {
5137
+ return function(data) {
5138
+ _this._sync_with_data_objects(data);
5139
+ return $(_this).trigger('objects_added', {
5140
+ objects: data
5141
+ });
5142
+ };
5143
+ })(this)), function() {
5144
+ return chr.showError('Error while loading data.');
5145
+ });
4852
5146
  };
4853
5147
 
4854
- return ArrayStore;
5148
+ return RestArrayStore;
4855
5149
 
4856
- })();
5150
+ })(ArrayStore);
4857
5151
 
4858
5152
  var extend = 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; },
4859
5153
  hasProp = {}.hasOwnProperty;
4860
5154
 
4861
- this.RestArrayStore = (function(superClass) {
4862
- extend(RestArrayStore, superClass);
5155
+ this.RestObjectStore = (function(superClass) {
5156
+ extend(RestObjectStore, superClass);
4863
5157
 
4864
- function RestArrayStore() {
4865
- return RestArrayStore.__super__.constructor.apply(this, arguments);
5158
+ function RestObjectStore() {
5159
+ return RestObjectStore.__super__.constructor.apply(this, arguments);
4866
5160
  }
4867
5161
 
4868
- RestArrayStore.prototype._initialize_database = function() {
5162
+ RestObjectStore.prototype._initialize_database = function() {
4869
5163
  this.dataFetchLock = false;
4870
5164
  return this.ajaxConfig = {};
4871
5165
  };
4872
5166
 
4873
- RestArrayStore.prototype._resource_url = function(type, id) {
4874
- var objectPath;
4875
- objectPath = id ? "/" + id : '';
4876
- return "" + this.config.path + objectPath;
5167
+ RestObjectStore.prototype._resource_url = function() {
5168
+ return this.config.path;
4877
5169
  };
4878
5170
 
4879
- RestArrayStore.prototype._ajax = function(type, id, data, success, error) {
5171
+ RestObjectStore.prototype._parse_form_object = function(serializedFormObject) {
5172
+ var fieldName, key, object, value;
5173
+ object = {};
5174
+ for (key in serializedFormObject) {
5175
+ value = serializedFormObject[key];
5176
+ fieldName = key.replace('[', '').replace(']', '');
5177
+ object[fieldName] = value;
5178
+ }
5179
+ return object;
5180
+ };
5181
+
5182
+ RestObjectStore.prototype._ajax = function(type, data, success, error) {
4880
5183
  var options;
4881
5184
  options = $.extend(this.ajaxConfig, {
4882
- url: this._resource_url(type, id),
5185
+ url: this._resource_url(),
4883
5186
  type: type,
4884
5187
  data: data,
4885
5188
  success: (function(_this) {
4886
5189
  return function(data, textStatus, jqXHR) {
4887
- _this.dataFetchLock = false;
4888
- return typeof success === "function" ? success(data) : void 0;
5190
+ if (typeof success === "function") {
5191
+ success(data);
5192
+ }
5193
+ return _this.dataFetchLock = false;
4889
5194
  };
4890
5195
  })(this),
4891
5196
  error: (function(_this) {
4892
5197
  return function(jqXHR, textStatus, errorThrown) {
4893
- _this.dataFetchLock = false;
4894
- return typeof error === "function" ? error(jqXHR.responseJSON) : void 0;
5198
+ if (typeof error === "function") {
5199
+ error(jqXHR.responseJSON);
5200
+ }
5201
+ return _this.dataFetchLock = false;
4895
5202
  };
4896
5203
  })(this)
4897
5204
  });
@@ -4899,64 +5206,46 @@ this.RestArrayStore = (function(superClass) {
4899
5206
  return $.ajax(options);
4900
5207
  };
4901
5208
 
4902
- RestArrayStore.prototype.load = function(success) {
4903
- return this._ajax('GET', null, {}, ((function(_this) {
4904
- return function(dataObject) {
4905
- var i, len, o;
4906
- if (dataObject.length > 0) {
4907
- for (i = 0, len = dataObject.length; i < len; i++) {
4908
- o = dataObject[i];
4909
- _this._add_data_object(o);
4910
- }
4911
- }
4912
- if (typeof success === "function") {
4913
- success();
4914
- }
4915
- return $(_this).trigger('objects_added');
4916
- };
4917
- })(this)));
4918
- };
4919
-
4920
- RestArrayStore.prototype.push = function(serializedFormObject, callbacks) {
4921
- var obj;
4922
- obj = this._parse_form_object(serializedFormObject);
4923
- return this._ajax('POST', null, obj, ((function(_this) {
4924
- return function(dataObject) {
4925
- if (_this.newItemOnTop) {
4926
- return _this._add_data_object_on_top(dataObject, callbacks.onSuccess);
4927
- } else {
4928
- return _this._add_data_object(dataObject, callbacks.onSuccess);
4929
- }
5209
+ RestObjectStore.prototype.loadObject = function(callbacks) {
5210
+ if (callbacks == null) {
5211
+ callbacks = {};
5212
+ }
5213
+ if (callbacks.onSuccess == null) {
5214
+ callbacks.onSuccess = $.noop;
5215
+ }
5216
+ if (callbacks.onError == null) {
5217
+ callbacks.onError = $.noop;
5218
+ }
5219
+ return this._ajax('GET', {}, ((function(_this) {
5220
+ return function(data) {
5221
+ return callbacks.onSuccess(data);
4930
5222
  };
4931
5223
  })(this)), callbacks.onError);
4932
5224
  };
4933
5225
 
4934
- RestArrayStore.prototype.update = function(id, serializedFormObject, callbacks) {
5226
+ RestObjectStore.prototype.update = function(id, serializedFormObject, callbacks) {
4935
5227
  var obj;
5228
+ if (callbacks == null) {
5229
+ callbacks = {};
5230
+ }
5231
+ if (callbacks.onSuccess == null) {
5232
+ callbacks.onSuccess = $.noop;
5233
+ }
5234
+ if (callbacks.onError == null) {
5235
+ callbacks.onError = $.noop;
5236
+ }
4936
5237
  obj = this._parse_form_object(serializedFormObject);
4937
- return this._ajax('PUT', id, obj, ((function(_this) {
4938
- return function(dataObject) {
4939
- return _this._update_data_object(id, dataObject, callbacks.onSuccess);
5238
+ return this._ajax('PUT', obj, ((function(_this) {
5239
+ return function(data) {
5240
+ _this._data = data;
5241
+ return callbacks.onSuccess(data);
4940
5242
  };
4941
5243
  })(this)), callbacks.onError);
4942
5244
  };
4943
5245
 
4944
- RestArrayStore.prototype.remove = function(id) {
4945
- return this._ajax('DELETE', id, {}, ((function(_this) {
4946
- return function() {
4947
- return _this._remove_data_object(id);
4948
- };
4949
- })(this)));
4950
- };
4951
-
4952
- RestArrayStore.prototype.reset = function(callback) {
4953
- this._reset_data();
4954
- return this.load(callback);
4955
- };
4956
-
4957
- return RestArrayStore;
5246
+ return RestObjectStore;
4958
5247
 
4959
- })(ArrayStore);
5248
+ })(ObjectStore);
4960
5249
 
4961
5250
  var extend = 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; },
4962
5251
  hasProp = {}.hasOwnProperty;
@@ -4978,10 +5267,68 @@ this.MongosteenArrayStore = (function(superClass) {
4978
5267
  this.searchable = (ref = this.config.searchable) != null ? ref : false;
4979
5268
  this.searchQuery = '';
4980
5269
  this.pagination = (ref1 = this.config.pagination) != null ? ref1 : true;
4981
- this.pagesCounter = 0;
4982
- if (this.config.data) {
4983
- return this.pagination = false;
5270
+ this.nextPage = 1;
5271
+ this.objectsPerPage = typeof _itemsPerPageRequest !== "undefined" && _itemsPerPageRequest !== null ? _itemsPerPageRequest : 20;
5272
+ if (this.pagination) {
5273
+ return this._bind_pagination_sync();
5274
+ }
5275
+ };
5276
+
5277
+ MongosteenArrayStore.prototype._bind_pagination_sync = function() {
5278
+ this.lastPageLoaded = false;
5279
+ $(this).on('object_added', (function(_this) {
5280
+ return function(e, data) {
5281
+ var new_object, new_object_position;
5282
+ if (!_this.lastPageLoaded) {
5283
+ new_object = data.object;
5284
+ new_object_position = data.position;
5285
+ if (new_object_position >= _this.objectsNumberForLoadedPages) {
5286
+ e.stopImmediatePropagation();
5287
+ return _this._remove_data_object(new_object._id);
5288
+ }
5289
+ }
5290
+ };
5291
+ })(this));
5292
+ $(this).on('object_changed', (function(_this) {
5293
+ return function(e, data) {
5294
+ var new_object, new_object_position;
5295
+ if (!_this.lastPageLoaded) {
5296
+ new_object = data.object;
5297
+ new_object_position = data.position;
5298
+ if (new_object_position >= _this.objectsNumberForLoadedPages - 1) {
5299
+ e.stopImmediatePropagation();
5300
+ return _this._remove_data_object(new_object._id);
5301
+ }
5302
+ }
5303
+ };
5304
+ })(this));
5305
+ return $(this).on('object_removed', (function(_this) {
5306
+ return function(e, data) {
5307
+ if (!_this.lastPageLoaded) {
5308
+ return _this._reload_current_page();
5309
+ }
5310
+ };
5311
+ })(this));
5312
+ };
5313
+
5314
+ MongosteenArrayStore.prototype._reload_current_page = function() {
5315
+ this.nextPage -= 1;
5316
+ return this.load();
5317
+ };
5318
+
5319
+ MongosteenArrayStore.prototype._udpate_next_page = function(data) {
5320
+ if (this.pagination) {
5321
+ if (data.length > 0) {
5322
+ this.lastPageLoaded = true;
5323
+ if (data.length === this.objectsPerPage) {
5324
+ this.nextPage += 1;
5325
+ this.lastPageLoaded = false;
5326
+ }
5327
+ } else {
5328
+ this.lastPageLoaded = true;
5329
+ }
4984
5330
  }
5331
+ return this.objectsNumberForLoadedPages = (this.nextPage - 1) * this.objectsPerPage;
4985
5332
  };
4986
5333
 
4987
5334
  MongosteenArrayStore.prototype._resource_url = function(type, id) {
@@ -5017,53 +5364,125 @@ this.MongosteenArrayStore = (function(superClass) {
5017
5364
  return formDataObject;
5018
5365
  };
5019
5366
 
5020
- MongosteenArrayStore.prototype.load = function(success) {
5367
+ MongosteenArrayStore.prototype.search = function(searchQuery) {
5368
+ this.searchQuery = searchQuery;
5369
+ this.nextPage = 1;
5370
+ this._reset_data();
5371
+ return this.load();
5372
+ };
5373
+
5374
+ MongosteenArrayStore.prototype.load = function(callbacks) {
5021
5375
  var params;
5376
+ if (callbacks == null) {
5377
+ callbacks = {};
5378
+ }
5379
+ if (callbacks.onSuccess == null) {
5380
+ callbacks.onSuccess = $.noop;
5381
+ }
5382
+ if (callbacks.onError == null) {
5383
+ callbacks.onError = $.noop;
5384
+ }
5022
5385
  params = {};
5023
5386
  if (this.pagination) {
5024
- params.page = this.pagesCounter + 1;
5025
- params.perPage = typeof _itemsPerPageRequest !== "undefined" && _itemsPerPageRequest !== null ? _itemsPerPageRequest : 20;
5387
+ params.page = this.nextPage;
5388
+ params.perPage = this.objectsPerPage;
5026
5389
  }
5027
5390
  if (this.searchable && this.searchQuery.length > 0) {
5028
5391
  params.search = this.searchQuery;
5029
5392
  }
5030
5393
  params = $.param(params);
5031
5394
  return this._ajax('GET', null, params, ((function(_this) {
5032
- return function(dataObject) {
5395
+ return function(data) {
5033
5396
  var i, len, o;
5034
- if (dataObject.length > 0) {
5035
- _this.pagesCounter = _this.pagesCounter + 1;
5036
- for (i = 0, len = dataObject.length; i < len; i++) {
5037
- o = dataObject[i];
5038
- _this._add_data_object(o);
5039
- }
5040
- }
5041
- if (typeof success === "function") {
5042
- success();
5397
+ _this._udpate_next_page(data);
5398
+ for (i = 0, len = data.length; i < len; i++) {
5399
+ o = data[i];
5400
+ _this._add_data_object(o);
5043
5401
  }
5044
- return $(_this).trigger('objects_added');
5402
+ callbacks.onSuccess(data);
5403
+ return $(_this).trigger('objects_added', {
5404
+ objects: data
5405
+ });
5045
5406
  };
5046
- })(this)));
5047
- };
5048
-
5049
- MongosteenArrayStore.prototype.search = function(searchQuery, callback) {
5050
- this.searchQuery = searchQuery;
5051
- this.pagesCounter = 0;
5052
- this._reset_data();
5053
- return this.load(callback);
5407
+ })(this)), callbacks.onError);
5054
5408
  };
5055
5409
 
5056
- MongosteenArrayStore.prototype.reset = function(callback) {
5410
+ MongosteenArrayStore.prototype.reset = function() {
5411
+ var params;
5057
5412
  this.searchQuery = '';
5058
- this.pagesCounter = 0;
5059
- this._reset_data();
5060
- return this.load(callback);
5413
+ this.nextPage = 1;
5414
+ params = {};
5415
+ if (this.pagination) {
5416
+ this.lastPageLoaded = false;
5417
+ params.page = this.nextPage;
5418
+ params.perPage = this.objectsPerPage;
5419
+ }
5420
+ params = $.param(params);
5421
+ return this._ajax('GET', null, params, ((function(_this) {
5422
+ return function(data) {
5423
+ _this._udpate_next_page(data);
5424
+ _this._sync_with_data_objects(data);
5425
+ return $(_this).trigger('objects_added', {
5426
+ objects: data
5427
+ });
5428
+ };
5429
+ })(this)), function() {
5430
+ return chr.showError('Error while loading data.');
5431
+ });
5061
5432
  };
5062
5433
 
5063
5434
  return MongosteenArrayStore;
5064
5435
 
5065
5436
  })(RestArrayStore);
5066
5437
 
5438
+ var extend = 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; },
5439
+ hasProp = {}.hasOwnProperty;
5440
+
5441
+ this.MongosteenObjectStore = (function(superClass) {
5442
+ extend(MongosteenObjectStore, superClass);
5443
+
5444
+ function MongosteenObjectStore() {
5445
+ return MongosteenObjectStore.__super__.constructor.apply(this, arguments);
5446
+ }
5447
+
5448
+ MongosteenObjectStore.prototype._initialize_database = function() {
5449
+ this.dataFetchLock = false;
5450
+ return this.ajaxConfig = {
5451
+ processData: false,
5452
+ contentType: false
5453
+ };
5454
+ };
5455
+
5456
+ MongosteenObjectStore.prototype._resource_url = function() {
5457
+ return this.config.path + ".json";
5458
+ };
5459
+
5460
+ MongosteenObjectStore.prototype._parse_form_object = function(serializedFormObject) {
5461
+ var attr_name, attr_value, formDataObject, i, len, value, values;
5462
+ formDataObject = new FormData();
5463
+ for (attr_name in serializedFormObject) {
5464
+ attr_value = serializedFormObject[attr_name];
5465
+ if (attr_name.indexOf('[__LIST__') > -1) {
5466
+ attr_name = attr_name.replace('__LIST__', '');
5467
+ values = attr_value.split(',');
5468
+ for (i = 0, len = values.length; i < len; i++) {
5469
+ value = values[i];
5470
+ formDataObject.append("" + this.config.resource + attr_name + "[]", value);
5471
+ }
5472
+ } else {
5473
+ if (attr_name.startsWith('__FILE__')) {
5474
+ attr_name = attr_name.replace('__FILE__', '');
5475
+ }
5476
+ formDataObject.append("" + this.config.resource + attr_name, attr_value);
5477
+ }
5478
+ }
5479
+ return formDataObject;
5480
+ };
5481
+
5482
+ return MongosteenObjectStore;
5483
+
5484
+ })(RestObjectStore);
5485
+
5067
5486
  this._last = function(array) {
5068
5487
  return array[array.length - 1];
5069
5488
  };
@@ -5141,54 +5560,34 @@ this._isMobile = function() {
5141
5560
  };
5142
5561
 
5143
5562
  this.Chr = (function() {
5144
- function Chr(config1) {
5145
- var config, name, ref, ref1;
5146
- this.config = config1;
5563
+ function Chr() {
5147
5564
  this.modules = {};
5148
- this.$el = $((ref = this.config.selector) != null ? ref : 'body');
5149
- this.$navBar = $("<nav class='sidebar'>");
5150
- this.$mainMenu = $("<div class='menu'>");
5151
- this.$navBar.append(this.$mainMenu);
5152
- this.$el.append(this.$navBar);
5153
- ref1 = this.config.modules;
5154
- for (name in ref1) {
5155
- config = ref1[name];
5156
- this.modules[name] = new Module(this, name, config);
5157
- }
5158
- $(document).on('click', 'a.silent', function(e) {
5159
- return window._skipHashchange = true;
5160
- });
5161
- window.onhashchange = (function(_this) {
5162
- return function() {
5163
- if (!window._skipHashchange) {
5164
- _this._navigate(location.hash);
5165
- }
5166
- return window._skipHashchange = false;
5167
- };
5168
- })(this);
5169
- if (!_isMobile()) {
5170
- window._skipHashchange = false;
5171
- this._navigate(location.hash !== '' ? location.hash : '#/' + Object.keys(this.modules)[0]);
5172
- }
5173
5565
  }
5174
5566
 
5175
- Chr.prototype.addMenuItem = function(moduleName, title) {
5176
- return this.$mainMenu.append("<a href='#/" + moduleName + "'>" + title + "</a>");
5567
+ Chr.prototype._unset_active_menu_items = function() {
5568
+ return $('.sidebar .menu a.active').removeClass('active');
5177
5569
  };
5178
5570
 
5179
- Chr.prototype.selectMenuItem = function(href) {
5180
- this.$mainMenu.children().removeClass('active');
5181
- return this.$mainMenu.children("a[href='#/" + href + "']").addClass('active');
5571
+ Chr.prototype.unsetActiveListItems = function() {
5572
+ return $('.list .items .item.active').removeClass('active');
5182
5573
  };
5183
5574
 
5184
- Chr.prototype.unselectMenuItem = function() {
5185
- return this.$mainMenu.children().removeClass('active');
5575
+ Chr.prototype._set_active_menu_item = function() {
5576
+ var a, currentModuleName, i, len, moduleName, ref;
5577
+ currentModuleName = window.location.hash.split('/')[1];
5578
+ ref = this.$mainMenu.children();
5579
+ for (i = 0, len = ref.length; i < len; i++) {
5580
+ a = ref[i];
5581
+ moduleName = $(a).attr('href').split('/')[1];
5582
+ if (currentModuleName === moduleName) {
5583
+ return $(a).addClass('active');
5584
+ }
5585
+ }
5186
5586
  };
5187
5587
 
5188
5588
  Chr.prototype._navigate = function(path) {
5189
- var config, crumb, crumbs, i, len, object, objectId, ref, results;
5589
+ var config, crumb, crumbs, i, len, objectId, ref, results;
5190
5590
  crumbs = path.split('/');
5191
- this.unselectMenuItem();
5192
5591
  if (this.module !== this.modules[crumbs[1]]) {
5193
5592
  if ((ref = this.module) != null) {
5194
5593
  ref.hide(path === '#/');
@@ -5207,14 +5606,11 @@ this.Chr = (function() {
5207
5606
  }
5208
5607
  if (crumb === 'view') {
5209
5608
  objectId = _last(crumbs);
5210
- return this.module.showViewWhenObjectsAreReady(objectId, config);
5609
+ return this.module.showViewByObjectId(objectId, config);
5211
5610
  }
5212
5611
  config = config.items[crumb];
5213
5612
  if (config.objectStore) {
5214
- object = $.extend({
5215
- _id: crumb
5216
- }, config.objectStore.get());
5217
- return this.module.showView(object, config, crumb.titleize());
5613
+ return this.module.showViewByObjectId('', config, crumb.titleize());
5218
5614
  } else {
5219
5615
  this.module.showNestedList(crumb);
5220
5616
  }
@@ -5230,6 +5626,61 @@ this.Chr = (function() {
5230
5626
  }
5231
5627
  };
5232
5628
 
5629
+ Chr.prototype.start = function(config1) {
5630
+ var config, name, ref, ref1;
5631
+ this.config = config1;
5632
+ this.$el = $((ref = this.config.selector) != null ? ref : 'body');
5633
+ this.$navBar = $("<nav class='sidebar'>");
5634
+ this.$mainMenu = $("<div class='menu'>");
5635
+ this.$navBar.append(this.$mainMenu);
5636
+ this.$el.append(this.$navBar);
5637
+ ref1 = this.config.modules;
5638
+ for (name in ref1) {
5639
+ config = ref1[name];
5640
+ this.modules[name] = new Module(this, name, config);
5641
+ }
5642
+ $(this).on('hashchange', (function(_this) {
5643
+ return function() {
5644
+ return _this._set_active_menu_item();
5645
+ };
5646
+ })(this));
5647
+ window.onhashchange = (function(_this) {
5648
+ return function() {
5649
+ _this._unset_active_menu_items();
5650
+ _this.unsetActiveListItems();
5651
+ if (!window._skipHashchange) {
5652
+ _this._navigate(location.hash);
5653
+ }
5654
+ window._skipHashchange = false;
5655
+ return $(_this).trigger('hashchange');
5656
+ };
5657
+ })(this);
5658
+ $(document).on('click', 'a.silent', function(e) {
5659
+ return window._skipHashchange = true;
5660
+ });
5661
+ window._skipHashchange = false;
5662
+ if (location.hash !== '') {
5663
+ this._navigate(location.hash);
5664
+ return $(this).trigger('hashchange');
5665
+ } else if (!_isMobile()) {
5666
+ return location.hash = '#/' + Object.keys(this.modules)[0];
5667
+ }
5668
+ };
5669
+
5670
+ Chr.prototype.addMenuItem = function(moduleName, title) {
5671
+ return this.$mainMenu.append("<a href='#/" + moduleName + "'>" + title + "</a>");
5672
+ };
5673
+
5674
+ Chr.prototype.showAlert = function(message) {
5675
+ return console.log('Alert: ' + message);
5676
+ };
5677
+
5678
+ Chr.prototype.showError = function(message) {
5679
+ return console.log('Error: ' + message);
5680
+ };
5681
+
5233
5682
  return Chr;
5234
5683
 
5235
5684
  })();
5685
+
5686
+ window.chr = new Chr();