chr 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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();