luca 0.9.1 → 0.9.2

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 (59) hide show
  1. data/CHANGELOG +9 -0
  2. data/ROADMAP +15 -3
  3. data/Rakefile +33 -7
  4. data/app.rb +0 -1
  5. data/assets/javascripts/dependencies.coffee +0 -1
  6. data/assets/javascripts/sandbox/application.coffee +1 -1
  7. data/assets/javascripts/sandbox/templates/main.luca +52 -34
  8. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +3 -1
  9. data/assets/stylesheets/bootstrap-responsive.min.css +0 -1
  10. data/assets/stylesheets/bootstrap.min.css +48 -29
  11. data/assets/stylesheets/sandbox/sandbox.scss +11 -28
  12. data/lib/luca/rails/version.rb +1 -1
  13. data/site/assets/dependencies.js +94 -0
  14. data/site/assets/glyphicons-halflings-white.png +0 -0
  15. data/site/assets/glyphicons-halflings.png +0 -0
  16. data/site/assets/luca-ui-bootstrap.css +1313 -0
  17. data/site/assets/luca-ui-bootstrap.js +9 -0
  18. data/site/assets/luca-ui-development-tools.css +224 -0
  19. data/site/assets/luca-ui-development-tools.js +18561 -0
  20. data/site/assets/sandbox.css +14 -0
  21. data/site/assets/sandbox.js +131 -0
  22. data/site/index.html +20 -0
  23. data/spec/core/collection_spec.coffee +6 -6
  24. data/spec/core/view_spec.coffee +1 -0
  25. data/spec/framework_spec.coffee +7 -0
  26. data/spec/managers/collection_manager_spec.coffee +2 -1
  27. data/src/components/application.coffee +8 -4
  28. data/src/components/collection_view.coffee +8 -2
  29. data/src/components/fields/checkbox_array.coffee +3 -1
  30. data/src/components/form_view.coffee +46 -21
  31. data/src/components/grid_view.coffee +2 -4
  32. data/src/components/nav_bar.coffee +3 -7
  33. data/src/containers/tab_view.coffee +15 -2
  34. data/src/containers/viewport.coffee +13 -4
  35. data/src/core/collection.coffee +68 -53
  36. data/src/core/core.coffee +7 -2
  37. data/src/core/panel.coffee +32 -17
  38. data/src/core/registry.coffee +11 -3
  39. data/src/core/util.coffee +17 -1
  40. data/src/core/view.coffee +6 -5
  41. data/src/framework.coffee +46 -2
  42. data/src/managers/collection_manager.coffee +22 -81
  43. data/src/stylesheets/components/checkbox_array.scss +5 -0
  44. data/src/templates/components/form_alert +0 -0
  45. data/src/templates/components/form_alert.luca +3 -0
  46. data/src/templates/containers/tab_view.luca +1 -1
  47. data/src/tools/console.coffee +3 -0
  48. data/vendor/assets/javascripts/luca-ui-base.js +266 -128
  49. data/vendor/assets/javascripts/luca-ui-development-tools.js +3 -161
  50. data/vendor/assets/javascripts/luca-ui-development-tools.min.js +15 -0
  51. data/vendor/assets/javascripts/luca-ui-spec.js +380 -176
  52. data/vendor/assets/javascripts/luca-ui.js +348 -166
  53. data/vendor/assets/javascripts/luca-ui.min.js +4 -3
  54. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +50 -29
  55. data/vendor/assets/stylesheets/luca-ui-spec.css +2 -0
  56. data/vendor/assets/stylesheets/luca-ui.css +2 -0
  57. metadata +16 -4
  58. data/src/templates/components/form_view.luca +0 -4
  59. data/src/tools/development_console.coffee +0 -147
@@ -18,7 +18,7 @@
18
18
  };
19
19
 
20
20
  _.extend(Luca, {
21
- VERSION: "0.9.1",
21
+ VERSION: "0.9.2",
22
22
  core: {},
23
23
  containers: {},
24
24
  components: {},
@@ -30,6 +30,8 @@
30
30
 
31
31
  _.extend(Luca, Backbone.Events);
32
32
 
33
+ Luca.autoRegister = true;
34
+
33
35
  Luca.developmentMode = false;
34
36
 
35
37
  Luca.enableGlobalObserver = false;
@@ -64,6 +66,10 @@
64
66
  return Luca.isBackboneModel(obj) || Luca.isBackboneView(obj) || Luca.isBackboneCollection(obj);
65
67
  };
66
68
 
69
+ Luca.isComponentPrototype = function(obj) {
70
+ return Luca.isViewPrototype(obj) || Luca.isModelPrototype(obj) || Luca.isCollectionPrototype(obj);
71
+ };
72
+
67
73
  Luca.isBackboneModel = function(obj) {
68
74
  return _.isFunction(obj != null ? obj.set : void 0) && _.isFunction(obj != null ? obj.get : void 0) && _.isObject(obj != null ? obj.attributes : void 0);
69
75
  };
@@ -76,6 +82,52 @@
76
82
  return _.isFunction(obj != null ? obj.fetch : void 0) && _.isFunction(obj != null ? obj.reset : void 0);
77
83
  };
78
84
 
85
+ Luca.isViewPrototype = function(obj) {
86
+ return (obj != null) && (obj.prototype != null) && (obj.prototype.make != null) && (obj.prototype.$ != null) && (obj.prototype.render != null);
87
+ };
88
+
89
+ Luca.isModelPrototype = function(obj) {
90
+ return (obj != null) && (typeof obj.prototype === "function" ? obj.prototype((obj.prototype.save != null) && (obj.prototype.changedAttributes != null)) : void 0);
91
+ };
92
+
93
+ Luca.isCollectionPrototype = function(obj) {
94
+ return (obj != null) && (obj.prototype != null) && !Luca.isModelPrototype(obj) && (obj.prototype.reset != null) && (obj.prototype.select != null) && (obj.prototype.reject != null);
95
+ };
96
+
97
+ Luca.inheritanceChain = function(obj) {
98
+ return _(Luca.parentClasses(obj)).map(function(className) {
99
+ return Luca.util.resolve(className);
100
+ });
101
+ };
102
+
103
+ Luca.parentClasses = function(obj) {
104
+ var classes, list, _ref;
105
+ list = [];
106
+ if (_.isString(obj)) obj = Luca.util.resolve(obj);
107
+ list.push(obj.displayName || ((_ref = obj.prototype) != null ? _ref.displayName : void 0) || Luca.parentClass(obj));
108
+ classes = (function() {
109
+ var _results;
110
+ _results = [];
111
+ while (!!(Luca.parentClass(obj) != null)) {
112
+ _results.push(obj = Luca.parentClass(obj));
113
+ }
114
+ return _results;
115
+ })();
116
+ list = list.concat(classes);
117
+ return _.uniq(list);
118
+ };
119
+
120
+ Luca.parentClass = function(obj) {
121
+ var list, _base, _ref;
122
+ list = [];
123
+ if (_.isString(obj)) obj = Luca.util.resolve(obj);
124
+ if (Luca.isComponent(obj)) {
125
+ return obj.displayName;
126
+ } else if (Luca.isComponentPrototype(obj)) {
127
+ return typeof (_base = obj.prototype)._superClass === "function" ? (_ref = _base._superClass()) != null ? _ref.displayName : void 0 : void 0;
128
+ }
129
+ };
130
+
79
131
  Luca.template = function(template_name, variables) {
80
132
  var jst, luca, needle, template, _ref;
81
133
  window.JST || (window.JST = {});
@@ -252,6 +304,30 @@
252
304
  return document.body.appendChild(script);
253
305
  };
254
306
 
307
+ Luca.util.make = Backbone.View.prototype.make;
308
+
309
+ Luca.util.label = function(contents, type, baseClass) {
310
+ var cssClass;
311
+ if (contents == null) contents = "";
312
+ if (baseClass == null) baseClass = "label";
313
+ cssClass = baseClass;
314
+ if (type != null) cssClass += " " + baseClass + "-" + type;
315
+ return Luca.util.make("span", {
316
+ "class": cssClass
317
+ }, contents);
318
+ };
319
+
320
+ Luca.util.badge = function(contents, type, baseClass) {
321
+ var cssClass;
322
+ if (contents == null) contents = "";
323
+ if (baseClass == null) baseClass = "badge";
324
+ cssClass = baseClass;
325
+ if (type != null) cssClass += " " + baseClass + "-" + type;
326
+ return Luca.util.make("span", {
327
+ "class": cssClass
328
+ }, contents);
329
+ };
330
+
255
331
  }).call(this);
256
332
  (function() {
257
333
  var DeferredBindingProxy, DefineProxy;
@@ -303,14 +379,21 @@
303
379
  };
304
380
 
305
381
  DefineProxy.prototype["with"] = function(properties) {
306
- var at;
382
+ var at, componentType;
307
383
  at = this.namespaced ? Luca.util.resolve(this.namespace, window || global) : window || global;
308
384
  if (this.namespaced && !(at != null)) {
309
385
  eval("(window||global)." + this.namespace + " = {}");
310
386
  at = Luca.util.resolve(this.namespace, window || global);
311
387
  }
312
388
  at[this.componentId] = Luca.extend(this.superClassName, this.componentName, properties);
313
- Luca.register(_.string.underscored(this.componentId), this.componentName);
389
+ if (Luca.autoRegister === true) {
390
+ if (Luca.isViewPrototype(at[this.componentId])) componentType = "view";
391
+ if (Luca.isCollectionPrototype(at[this.componentId])) {
392
+ componentType = "collection";
393
+ }
394
+ if (Luca.isModelPrototype(at[this.componentId])) componentType = "model";
395
+ Luca.register(_.string.underscored(this.componentId), this.componentName, componentType);
396
+ }
314
397
  return at[this.componentId];
315
398
  };
316
399
 
@@ -448,6 +531,8 @@
448
531
 
449
532
  registry = {
450
533
  classes: {},
534
+ model_classes: {},
535
+ collection_classes: {},
451
536
  namespaces: ['Luca.containers', 'Luca.components']
452
537
  };
453
538
 
@@ -458,9 +543,17 @@
458
543
 
459
544
  Luca.defaultComponentType = 'view';
460
545
 
461
- Luca.register = function(component, prototypeName) {
546
+ Luca.register = function(component, prototypeName, componentType) {
547
+ if (componentType == null) componentType = "view";
462
548
  Luca.trigger("component:registered", component, prototypeName);
463
- return registry.classes[component] = prototypeName;
549
+ switch (componentType) {
550
+ case "model":
551
+ return registry.model_classes[component] = prototypeName;
552
+ case "collection":
553
+ return registry.model_classes[component] = prototypeName;
554
+ default:
555
+ return registry.classes[component] = prototypeName;
556
+ }
464
557
  };
465
558
 
466
559
  Luca.development_mode_register = function(component, prototypeName) {
@@ -599,7 +692,7 @@
599
692
  return this.delegateEvents();
600
693
  },
601
694
  $wrap: function(wrapper) {
602
- if (!wrapper.match(/[<>]/)) {
695
+ if (_.isString(wrapper) && !wrapper.match(/[<>]/)) {
603
696
  wrapper = this.make("div", {
604
697
  "class": wrapper
605
698
  });
@@ -639,8 +732,8 @@
639
732
  });
640
733
  },
641
734
  getCollectionManager: function() {
642
- var _ref;
643
- return this.collectionManager || ((_ref = Luca.CollectionManager.get) != null ? _ref.call() : void 0);
735
+ var _base;
736
+ return this.collectionManager || (typeof (_base = Luca.CollectionManager).get === "function" ? _base.get() : void 0);
644
737
  },
645
738
  registerCollectionEvents: function() {
646
739
  var manager,
@@ -722,7 +815,7 @@
722
815
  _base = definition.render;
723
816
  _base || (_base = Luca.View.prototype.$attach);
724
817
  definition.render = function() {
725
- var autoTrigger, fn, target, trigger, view,
818
+ var autoTrigger, deferred, fn, target, trigger, view,
726
819
  _this = this;
727
820
  view = this;
728
821
  if (this.deferrable) {
@@ -732,10 +825,11 @@
732
825
  }
733
826
  target || (target = this.deferrable);
734
827
  trigger = this.deferrable_event ? this.deferrable_event : "reset";
735
- view.defer(function() {
828
+ deferred = function() {
736
829
  _base.call(view);
737
830
  return view.trigger("after:render", view);
738
- }).until(target, trigger);
831
+ };
832
+ view.defer(deferred).until(target, trigger);
739
833
  view.trigger("before:render", this);
740
834
  autoTrigger = this.deferrable_trigger || this.deferUntil;
741
835
  if (!(autoTrigger != null)) {
@@ -809,72 +903,7 @@
809
903
 
810
904
  _.def("Luca.Collection")["extends"](source)["with"]({
811
905
  cachedMethods: [],
812
- restoreMethodCache: function() {
813
- var config, name, _ref, _results;
814
- _ref = this._methodCache;
815
- _results = [];
816
- for (name in _ref) {
817
- config = _ref[name];
818
- if (config.original != null) {
819
- config.args = void 0;
820
- _results.push(this[name] = config.original);
821
- } else {
822
- _results.push(void 0);
823
- }
824
- }
825
- return _results;
826
- },
827
- clearMethodCache: function(method) {
828
- return this._methodCache[method].value = void 0;
829
- },
830
- clearAllMethodsCache: function() {
831
- var config, name, _ref, _results;
832
- _ref = this._methodCache;
833
- _results = [];
834
- for (name in _ref) {
835
- config = _ref[name];
836
- _results.push(this.clearMethodCache(name));
837
- }
838
- return _results;
839
- },
840
- setupMethodCaching: function() {
841
- var cache, collection, membershipEvents;
842
- collection = this;
843
- membershipEvents = ["reset", "add", "remove"];
844
- cache = this._methodCache = {};
845
- return _(this.cachedMethods).each(function(method) {
846
- var dependencies, dependency, membershipEvent, _i, _j, _len, _len2, _ref, _results;
847
- cache[method] = {
848
- name: method,
849
- original: collection[method],
850
- value: void 0
851
- };
852
- collection[method] = function() {
853
- var _base;
854
- return (_base = cache[method]).value || (_base.value = cache[method].original.apply(collection, arguments));
855
- };
856
- for (_i = 0, _len = membershipEvents.length; _i < _len; _i++) {
857
- membershipEvent = membershipEvents[_i];
858
- collection.bind(membershipEvent, function() {
859
- return collection.clearAllMethodsCache();
860
- });
861
- }
862
- dependencies = method.split(':')[1];
863
- if (dependencies) {
864
- _ref = dependencies.split(",");
865
- _results = [];
866
- for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
867
- dependency = _ref[_j];
868
- _results.push(collection.bind("change:" + dependency, function() {
869
- return collection.clearMethodCache({
870
- method: method
871
- });
872
- }));
873
- }
874
- return _results;
875
- }
876
- });
877
- },
906
+ remoteFilter: false,
878
907
  initialize: function(models, options) {
879
908
  var table,
880
909
  _this = this;
@@ -894,6 +923,7 @@
894
923
  }
895
924
  this.name || (this.name = this.registerAs);
896
925
  this.manager || (this.manager = this.registerWith);
926
+ this.manager = _.isFunction(this.manager) ? this.manager() : this.manager;
897
927
  if (this.name && !this.manager) this.manager = Luca.CollectionManager.get();
898
928
  if (this.manager) {
899
929
  this.name || (this.name = this.cache_key());
@@ -957,14 +987,13 @@
957
987
  return _.uniq(parts).join("&");
958
988
  },
959
989
  resetFilter: function() {
960
- this.base_params = Luca.Collection.baseParams();
990
+ this.base_params = _(Luca.Collection.baseParams()).clone();
961
991
  return this;
962
992
  },
963
- remoteFilter: true,
964
993
  applyFilter: function(filter, options) {
965
994
  if (filter == null) filter = {};
966
995
  if (options == null) options = {};
967
- if ((options.remote != null) === true) {
996
+ if ((options.remote != null) === true || this.remoteFilter === true) {
968
997
  this.applyParams(filter);
969
998
  return this.fetch(_.extend(options, {
970
999
  refresh: true
@@ -974,8 +1003,9 @@
974
1003
  }
975
1004
  },
976
1005
  applyParams: function(params) {
977
- this.base_params || (this.base_params = _(Luca.Collection.baseParams()).clone());
978
- return _.extend(this.base_params, params);
1006
+ this.base_params = _(Luca.Collection.baseParams()).clone();
1007
+ _.extend(this.base_params, params);
1008
+ return this;
979
1009
  },
980
1010
  register: function(collectionManager, key, collection) {
981
1011
  if (collectionManager == null) {
@@ -1059,7 +1089,7 @@
1059
1089
  scope = options.scope || this;
1060
1090
  if (this.length > 0 && !this.fetching) fn.apply(scope, [this]);
1061
1091
  this.bind("reset", function(collection) {
1062
- return fn.apply(scope, [collection]);
1092
+ return fn.call(scope, collection);
1063
1093
  });
1064
1094
  if (!(this.fetching === true || !options.autoFetch || this.length > 0)) {
1065
1095
  return this.fetch();
@@ -1074,6 +1104,81 @@
1074
1104
  Luca.Collection.cache(this.bootstrap_cache_key, models);
1075
1105
  }
1076
1106
  return models;
1107
+ },
1108
+ restoreMethodCache: function() {
1109
+ var config, name, _ref, _results;
1110
+ _ref = this._methodCache;
1111
+ _results = [];
1112
+ for (name in _ref) {
1113
+ config = _ref[name];
1114
+ if (config.original != null) {
1115
+ config.args = void 0;
1116
+ _results.push(this[name] = config.original);
1117
+ } else {
1118
+ _results.push(void 0);
1119
+ }
1120
+ }
1121
+ return _results;
1122
+ },
1123
+ clearMethodCache: function(method) {
1124
+ return this._methodCache[method].value = void 0;
1125
+ },
1126
+ clearAllMethodsCache: function() {
1127
+ var config, name, _ref, _results;
1128
+ _ref = this._methodCache;
1129
+ _results = [];
1130
+ for (name in _ref) {
1131
+ config = _ref[name];
1132
+ _results.push(this.clearMethodCache(name));
1133
+ }
1134
+ return _results;
1135
+ },
1136
+ setupMethodCaching: function() {
1137
+ var cache, collection, membershipEvents;
1138
+ collection = this;
1139
+ membershipEvents = ["reset", "add", "remove"];
1140
+ cache = this._methodCache = {};
1141
+ return _(this.cachedMethods).each(function(method) {
1142
+ var dependencies, dependency, membershipEvent, _i, _j, _len, _len2, _ref, _results;
1143
+ cache[method] = {
1144
+ name: method,
1145
+ original: collection[method],
1146
+ value: void 0
1147
+ };
1148
+ collection[method] = function() {
1149
+ var _base;
1150
+ return (_base = cache[method]).value || (_base.value = cache[method].original.apply(collection, arguments));
1151
+ };
1152
+ for (_i = 0, _len = membershipEvents.length; _i < _len; _i++) {
1153
+ membershipEvent = membershipEvents[_i];
1154
+ collection.bind(membershipEvent, function() {
1155
+ return collection.clearAllMethodsCache();
1156
+ });
1157
+ }
1158
+ dependencies = method.split(':')[1];
1159
+ if (dependencies) {
1160
+ _ref = dependencies.split(",");
1161
+ _results = [];
1162
+ for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
1163
+ dependency = _ref[_j];
1164
+ _results.push(collection.bind("change:" + dependency, function() {
1165
+ return collection.clearMethodCache({
1166
+ method: method
1167
+ });
1168
+ }));
1169
+ }
1170
+ return _results;
1171
+ }
1172
+ });
1173
+ },
1174
+ query: function(filter, options) {
1175
+ if (filter == null) filter = {};
1176
+ if (options == null) options = {};
1177
+ if (Backbone.QueryCollection != null) {
1178
+ return Backbone.QueryCollection.prototype.query.apply(this, arguments);
1179
+ } else {
1180
+ return this.models;
1181
+ }
1077
1182
  }
1078
1183
  });
1079
1184
 
@@ -1092,7 +1197,7 @@
1092
1197
  Luca.Collection.baseParams = function(obj) {
1093
1198
  if (obj) return Luca.Collection._baseParams = obj;
1094
1199
  if (_.isFunction(Luca.Collection._baseParams)) {
1095
- return Luca.Collection._baseParams.call();
1200
+ return Luca.Collection._baseParams();
1096
1201
  }
1097
1202
  if (_.isObject(Luca.Collection._baseParams)) {
1098
1203
  return Luca.Collection._baseParams;
@@ -1114,7 +1219,7 @@
1114
1219
  (function() {
1115
1220
  var attachToolbar;
1116
1221
 
1117
- attachToolbar = function(config) {
1222
+ attachToolbar = function(config, targetEl) {
1118
1223
  var action, container, hasBody, id, toolbar;
1119
1224
  if (config == null) config = {};
1120
1225
  config.orientation || (config.orientation = "top");
@@ -1145,7 +1250,7 @@
1145
1250
  }
1146
1251
  }
1147
1252
  })();
1148
- return this.$bodyEl()[action](container);
1253
+ return (targetEl || this.$bodyEl())[action](container);
1149
1254
  };
1150
1255
 
1151
1256
  _.def("Luca.components.Panel")["extends"]("Luca.View")["with"]({
@@ -1153,10 +1258,12 @@
1153
1258
  bottomToolbar: void 0,
1154
1259
  loadMask: false,
1155
1260
  loadMaskTemplate: ["components/load_mask"],
1261
+ loadMaskTimeout: 3000,
1156
1262
  initialize: function(options) {
1157
1263
  var _this = this;
1158
1264
  this.options = options != null ? options : {};
1159
1265
  Luca.View.prototype.initialize.apply(this, arguments);
1266
+ _.bindAll(this, "applyLoadMask", "disableLoadMask");
1160
1267
  if (this.loadMask === true) {
1161
1268
  this.defer(function() {
1162
1269
  _this.$el.addClass('with-mask');
@@ -1176,25 +1283,35 @@
1176
1283
  return this.$bodyEl();
1177
1284
  }
1178
1285
  },
1179
- applyLoadMask: function() {
1286
+ disableLoadMask: function() {
1287
+ this.$('.load-mask .bar').css("width", "100%");
1288
+ this.$('.load-mask').hide();
1289
+ return clearInterval(this.loadMaskInterval);
1290
+ },
1291
+ enableLoadMask: function() {
1180
1292
  var maxWidth,
1181
1293
  _this = this;
1294
+ this.$('.load-mask').show().find('.bar').css("width", "0%");
1295
+ maxWidth = this.$('.load-mask .progress').width();
1296
+ if (maxWidth < 20 && (maxWidth = this.$el.width()) < 20) {
1297
+ maxWidth = this.$el.parent().width();
1298
+ }
1299
+ this.loadMaskInterval = setInterval(function() {
1300
+ var currentWidth, newWidth;
1301
+ currentWidth = _this.$('.load-mask .bar').width();
1302
+ newWidth = currentWidth + 12;
1303
+ return _this.$('.load-mask .bar').css('width', newWidth);
1304
+ }, 200);
1305
+ if (this.loadMaskTimeout == null) return;
1306
+ return _.delay(function() {
1307
+ return _this.disableLoadMask();
1308
+ }, this.loadMaskTimeout);
1309
+ },
1310
+ applyLoadMask: function() {
1182
1311
  if (this.$('.load-mask').is(":visible")) {
1183
- this.$('.load-mask .bar').css("width", "100%");
1184
- this.$('.load-mask').hide();
1185
- return clearInterval(this.loadMaskInterval);
1312
+ return this.disableLoadMask();
1186
1313
  } else {
1187
- this.$('.load-mask').show().find('.bar').css("width", "0%");
1188
- maxWidth = this.$('.load-mask .progress').width();
1189
- if (maxWidth < 20 && (maxWidth = this.$el.width()) < 20) {
1190
- maxWidth = this.$el.parent().width();
1191
- }
1192
- return this.loadMaskInterval = setInterval(function() {
1193
- var currentWidth, newWidth;
1194
- currentWidth = _this.$('.load-mask .bar').width();
1195
- newWidth = currentWidth + 12;
1196
- return _this.$('.load-mask .bar').css('width', newWidth);
1197
- }, 200);
1314
+ return this.enableLoadMask();
1198
1315
  }
1199
1316
  },
1200
1317
  applyStyles: function(styles, body) {
@@ -1235,7 +1352,7 @@
1235
1352
  return $(this.el);
1236
1353
  },
1237
1354
  $wrap: function(wrapper) {
1238
- if (!wrapper.match(/[<>]/)) {
1355
+ if (_.isString(wrapper) && !wrapper.match(/[<>]/)) {
1239
1356
  wrapper = this.make("div", {
1240
1357
  "class": wrapper
1241
1358
  });
@@ -1266,7 +1383,7 @@
1266
1383
  if (config == null) config = {};
1267
1384
  config.parent = this;
1268
1385
  config.orientation = orientation;
1269
- return attachToolbar.call(this, config);
1386
+ return attachToolbar.call(this, config, config.targetEl);
1270
1387
  }
1271
1388
  });
1272
1389
 
@@ -1383,7 +1500,7 @@
1383
1500
  }).call(this);
1384
1501
  (function() {
1385
1502
  Luca.templates || (Luca.templates = {});
1386
- Luca.templates["components/form_view"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<div class=\'form-view-panel\'>\n <ul class=\'form-view-flash-container\'></ul>\n <div class=\'form-view-body\'></div>\n</div>\n');}return __p.join('');};
1503
+ Luca.templates["components/form_alert"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<div class=\'', className ,'\'>\n <a class=\'close\' data-dismiss=\'alert\' href=\'#\'>x</a>\n ', message ,'\n</div>\n');}return __p.join('');};
1387
1504
  }).call(this);
1388
1505
  (function() {
1389
1506
  Luca.templates || (Luca.templates = {});
@@ -1411,7 +1528,7 @@
1411
1528
  }).call(this);
1412
1529
  (function() {
1413
1530
  Luca.templates || (Luca.templates = {});
1414
- Luca.templates["containers/tab_view"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<ul class=\'nav nav-tabs\' id=\'', cid ,'-tabs-selector\'></ul>\n<div class=\'tab-content\' id=\'', cid ,'-tab-view-content\'></div>\n');}return __p.join('');};
1531
+ Luca.templates["containers/tab_view"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<ul class=\'nav ', navClass ,'\' id=\'', cid ,'-tabs-selector\'></ul>\n<div class=\'tab-content\' id=\'', cid ,'-tab-view-content\'></div>\n');}return __p.join('');};
1415
1532
  }).call(this);
1416
1533
  (function() {
1417
1534
  Luca.templates || (Luca.templates = {});
@@ -1816,20 +1933,30 @@
1816
1933
 
1817
1934
  }).call(this);
1818
1935
  (function() {
1819
- var instances;
1820
-
1821
- instances = [];
1822
1936
 
1823
1937
  Luca.CollectionManager = (function() {
1824
1938
 
1939
+ CollectionManager.prototype.name = "primary";
1940
+
1825
1941
  CollectionManager.prototype.__collections = {};
1826
1942
 
1827
1943
  function CollectionManager(options) {
1944
+ var existing, manager, _base, _base2;
1828
1945
  this.options = options != null ? options : {};
1829
1946
  _.extend(this, this.options);
1947
+ manager = this;
1948
+ if (existing = typeof (_base = Luca.CollectionManager).get === "function" ? _base.get(this.name) : void 0) {
1949
+ throw 'Attempt to create a collection manager with a name which already exists';
1950
+ }
1951
+ (_base2 = Luca.CollectionManager).instances || (_base2.instances = {});
1830
1952
  _.extend(this, Backbone.Events);
1831
- instances.push(this);
1832
- this.state = new Backbone.Model;
1953
+ _.extend(this, Luca.Events);
1954
+ Luca.CollectionManager.instances[this.name] = manager;
1955
+ Luca.CollectionManager.get = function(name) {
1956
+ if (name == null) return manager;
1957
+ return Luca.CollectionManager.instances[name];
1958
+ };
1959
+ this.state = new Luca.Model();
1833
1960
  if (this.initialCollections) {
1834
1961
  this.state.set({
1835
1962
  loaded_collections_count: 0,
@@ -1947,15 +2074,7 @@
1947
2074
  })();
1948
2075
 
1949
2076
  Luca.CollectionManager.destroyAll = function() {
1950
- return instances = [];
1951
- };
1952
-
1953
- Luca.CollectionManager.instances = function() {
1954
- return instances;
1955
- };
1956
-
1957
- Luca.CollectionManager.get = function() {
1958
- return _(instances).last();
2077
+ return Luca.CollectionManager.instances = {};
1959
2078
  };
1960
2079
 
1961
2080
  }).call(this);
@@ -2371,10 +2490,12 @@
2371
2490
  className: 'luca-ui-tab-view tabbable',
2372
2491
  tab_position: 'top',
2373
2492
  tabVerticalOffset: '50px',
2493
+ navClass: "nav-tabs",
2374
2494
  bodyTemplate: "containers/tab_view",
2375
2495
  bodyEl: "div.tab-content",
2376
2496
  initialize: function(options) {
2377
2497
  this.options = options != null ? options : {};
2498
+ if (this.navStyle === "list") this.navClass = "nav-list";
2378
2499
  Luca.containers.CardView.prototype.initialize.apply(this, arguments);
2379
2500
  _.bindAll(this, "select", "highlightSelectedTab");
2380
2501
  this.setupHooks(this.hooks);
@@ -2405,12 +2526,21 @@
2405
2526
  var tabView;
2406
2527
  tabView = this;
2407
2528
  return this.each(function(component, index) {
2408
- var selector;
2529
+ var icon, link, selector, _ref;
2530
+ if (component.tabIcon) icon = "<i class='icon-" + component.tabIcon;
2531
+ link = "<a href='#'>" + (icon || '') + " " + component.title + "</a>";
2409
2532
  selector = tabView.make("li", {
2410
2533
  "class": "tab-selector",
2411
2534
  "data-target": index
2412
- }, "<a>" + component.title + "</a>");
2413
- return tabView.tabContainer().append(selector);
2535
+ }, link);
2536
+ tabView.tabContainer().append(selector);
2537
+ if ((component.navHeading != null) && !((_ref = tabView.navHeadings) != null ? _ref[component.navHeading] : void 0)) {
2538
+ $(selector).before(tabView.make('li', {
2539
+ "class": "nav-header"
2540
+ }, component.navHeading));
2541
+ tabView.navHeadings || (tabView.navHeadings = {});
2542
+ return tabView.navHeadings[component.navHeading] = true;
2543
+ }
2414
2544
  });
2415
2545
  },
2416
2546
  highlightSelectedTab: function() {
@@ -2419,6 +2549,7 @@
2419
2549
  },
2420
2550
  select: function(e) {
2421
2551
  var me, my;
2552
+ e.preventDefault();
2422
2553
  me = my = $(e.target);
2423
2554
  this.trigger("before:select", this);
2424
2555
  this.activate(my.parent().data('target'));
@@ -2434,7 +2565,7 @@
2434
2565
  return $("#" + this.cid + "-tabs-selector");
2435
2566
  },
2436
2567
  tabContainer: function() {
2437
- return this.$('ul.nav-tabs', this.tabContainerWrapper());
2568
+ return this.$("ul." + this.navClass, this.tabContainerWrapper());
2438
2569
  },
2439
2570
  tabSelectors: function() {
2440
2571
  return this.$('li.tab-selector', this.tabContainer());
@@ -2452,11 +2583,12 @@
2452
2583
  wrapperClass: 'row',
2453
2584
  initialize: function(options) {
2454
2585
  this.options = options != null ? options : {};
2455
- Luca.core.Container.prototype.initialize.apply(this, arguments);
2586
+ _.extend(this, this.options);
2456
2587
  if (Luca.enableBootstrap === true) {
2457
2588
  if (this.fluid === true) this.wrapperClass = "row-fluid";
2458
- this.$el.wrap("<div class='" + this.wrapperClass + "' />").addClass('span12');
2589
+ this.$wrap(this.wrapperClass);
2459
2590
  }
2591
+ Luca.core.Container.prototype.initialize.apply(this, arguments);
2460
2592
  if (this.fullscreen) return $('html,body').addClass('luca-ui-fullscreen');
2461
2593
  },
2462
2594
  beforeRender: function() {
@@ -2464,12 +2596,18 @@
2464
2596
  if ((_ref = Luca.containers.CardView.prototype.beforeRender) != null) {
2465
2597
  _ref.apply(this, arguments);
2466
2598
  }
2467
- if (Luca.enableBootstrap && this.topNav && this.fullscreen) {
2468
- $('body').css('padding', '40px');
2469
- }
2470
2599
  if (this.topNav != null) this.renderTopNavigation();
2471
2600
  if (this.bottomNav != null) return this.renderBottomNavigation();
2472
2601
  },
2602
+ afterRender: function() {
2603
+ var _ref;
2604
+ if ((_ref = Luca.containers.CardView.prototype.after) != null) {
2605
+ _ref.apply(this, arguments);
2606
+ }
2607
+ if (Luca.enableBootstrap === true) {
2608
+ return this.$el.children().wrap('<div class="container" />');
2609
+ }
2610
+ },
2473
2611
  renderTopNavigation: function() {
2474
2612
  var _base;
2475
2613
  if (this.topNav == null) return;
@@ -2510,6 +2648,7 @@
2510
2648
  _.def('Luca.Application')["extends"]('Luca.containers.Viewport')["with"]({
2511
2649
  autoStartHistory: true,
2512
2650
  useCollectionManager: true,
2651
+ collectionManagerClass: "Luca.CollectionManager",
2513
2652
  plugin: false,
2514
2653
  useController: true,
2515
2654
  components: [
@@ -2534,13 +2673,16 @@
2534
2673
  }
2535
2674
  ];
2536
2675
  if (this.useCollectionManager === true) {
2676
+ if (_.isString(this.collectionManagerClass)) {
2677
+ this.collectionManagerClass = Luca.util.resolve(this.collectionManagerClass);
2678
+ }
2537
2679
  this.collectionManager || (this.collectionManager = typeof (_base = Luca.CollectionManager).get === "function" ? _base.get() : void 0);
2538
- this.collectionManager || (this.collectionManager = new Luca.CollectionManager(this.collectionManagerOptions || (this.collectionManagerOptions = {})));
2680
+ this.collectionManager || (this.collectionManager = new this.collectionManagerClass(this.collectionManagerOptions || (this.collectionManagerOptions = {})));
2539
2681
  }
2540
- this.state = new Backbone.Model(this.defaultState);
2541
- this.bind("ready", function() {
2682
+ this.state = new Luca.Model(this.defaultState);
2683
+ this.defer(function() {
2542
2684
  return _this.render();
2543
- });
2685
+ }).until("ready");
2544
2686
  if (this.useKeyRouter === true && (this.keyEvents != null)) {
2545
2687
  this.setupKeyRouter();
2546
2688
  }
@@ -2733,13 +2875,16 @@
2733
2875
  this.options = options != null ? options : {};
2734
2876
  _.extend(this, this.options);
2735
2877
  _.bindAll(this, "refresh");
2736
- if (this.collection == null) {
2878
+ if (!((this.collection != null) || this.options.collection)) {
2737
2879
  throw "Collection Views must specify a collection";
2738
2880
  }
2739
2881
  if (!((this.itemTemplate != null) || (this.itemRenderer != null) || (this.itemProperty != null))) {
2740
2882
  throw "Collection Views must specify an item template or item renderer function";
2741
2883
  }
2742
2884
  Luca.components.Panel.prototype.initialize.apply(this, arguments);
2885
+ if (_.isString(this.collection) && Luca.CollectionManager.get()) {
2886
+ this.collection = Luca.CollectionManager.get().get(this.collection);
2887
+ }
2743
2888
  if (Luca.isBackboneCollection(this.collection)) {
2744
2889
  this.collection.bind("reset", this.refresh);
2745
2890
  this.collection.bind("add", this.refresh);
@@ -2762,8 +2907,10 @@
2762
2907
  content = this.itemRenderer.call(this, item);
2763
2908
  }
2764
2909
  if (this.itemProperty) {
2765
- return content = _.isFunction(this.itemProperty) ? this.itemProperty() : item.model.get(this.itemProperty) || item.model[this.itemProperty];
2910
+ content = item.model.get(this.itemProperty) || item.model[this.itemProperty];
2911
+ if (_.isFunction(content)) content = content();
2766
2912
  }
2913
+ return content;
2767
2914
  },
2768
2915
  makeItem: function(model, index) {
2769
2916
  var item;
@@ -2896,6 +3043,7 @@
2896
3043
  _.def('Luca.fields.CheckboxArray')["extends"]('Luca.core.Field')["with"]({
2897
3044
  version: 2,
2898
3045
  template: "fields/checkbox_array",
3046
+ className: "luca-ui-checkbox-array",
2899
3047
  events: {
2900
3048
  "click input": "clickHandler"
2901
3049
  },
@@ -2952,6 +3100,7 @@
2952
3100
  input_id = _.uniqueId("" + _this.cid + "_checkbox");
2953
3101
  inputElement = make("input", {
2954
3102
  type: "checkbox",
3103
+ "class": "array-checkbox",
2955
3104
  name: _this.input_name,
2956
3105
  value: value,
2957
3106
  id: input_id
@@ -3333,6 +3482,27 @@
3333
3482
 
3334
3483
  }).call(this);
3335
3484
  (function() {
3485
+ var defaultToolbar;
3486
+
3487
+ defaultToolbar = {
3488
+ buttons: [
3489
+ {
3490
+ icon: "remove-sign",
3491
+ label: "Reset",
3492
+ eventId: "click:reset",
3493
+ className: "reset-button",
3494
+ align: 'right'
3495
+ }, {
3496
+ icon: "ok-sign",
3497
+ white: true,
3498
+ label: "Save Changes",
3499
+ eventId: "click:submit",
3500
+ color: "success",
3501
+ className: 'submit-button',
3502
+ align: 'right'
3503
+ }
3504
+ ]
3505
+ };
3336
3506
 
3337
3507
  _.def("Luca.components.FormView")["extends"]('Luca.core.Container')["with"]({
3338
3508
  tagName: 'form',
@@ -3345,7 +3515,6 @@
3345
3515
  toolbar: true,
3346
3516
  legend: "",
3347
3517
  bodyClassName: "form-view-body",
3348
- bodyTemplate: ["components/form_view"],
3349
3518
  initialize: function(options) {
3350
3519
  this.options = options != null ? options : {};
3351
3520
  if (this.loadMask == null) this.loadMask = Luca.enableBootstrap;
@@ -3354,28 +3523,18 @@
3354
3523
  this.state || (this.state = new Backbone.Model);
3355
3524
  this.setupHooks(this.hooks);
3356
3525
  this.applyStyleClasses();
3357
- if (this.toolbar === true && !((this.bottomToolbar != null) || (this.topToolbar != null))) {
3358
- return this.bottomToolbar = {
3359
- buttons: [
3360
- {
3361
- icon: "remove-sign",
3362
- label: "Reset",
3363
- eventId: "click:reset",
3364
- className: "reset-button",
3365
- align: 'right'
3366
- }, {
3367
- icon: "ok-sign",
3368
- white: true,
3369
- label: "Save Changes",
3370
- eventId: "click:submit",
3371
- color: "success",
3372
- className: 'submit-button',
3373
- align: 'right'
3374
- }
3375
- ]
3376
- };
3526
+ if (this.toolbar !== false && (!this.topToolbar && !this.bottomToolbar)) {
3527
+ if (this.toolbar === "both" || this.toolbar === "top") {
3528
+ this.topToolbar = this.getDefaultToolbar();
3529
+ }
3530
+ if (this.toolbar !== "top") {
3531
+ return this.bottomToolbar = this.getDefaultToolbar();
3532
+ }
3377
3533
  }
3378
3534
  },
3535
+ getDefaultToolbar: function() {
3536
+ return defaultToolbar;
3537
+ },
3379
3538
  applyStyleClasses: function() {
3380
3539
  if (Luca.enableBootstrap) this.applyBootstrapStyleClasses();
3381
3540
  if (this.labelAlign) this.$el.addClass("label-align-" + this.labelAlign);
@@ -3390,14 +3549,14 @@
3390
3549
  },
3391
3550
  resetHandler: function(e) {
3392
3551
  var me, my;
3393
- me = my = $(e.currentTarget);
3552
+ me = my = $(e != null ? e.target : void 0);
3394
3553
  this.trigger("before:reset", this);
3395
3554
  this.reset();
3396
3555
  return this.trigger("after:reset", this);
3397
3556
  },
3398
3557
  submitHandler: function(e) {
3399
3558
  var me, my;
3400
- me = my = $(e.currentTarget);
3559
+ me = my = $(e != null ? e.target : void 0);
3401
3560
  this.trigger("before:submit", this);
3402
3561
  if (this.loadMask === true) this.trigger("enable:loadmask", this);
3403
3562
  if (this.hasModel()) return this.submit();
@@ -3544,6 +3703,32 @@
3544
3703
  setLegend: function(legend) {
3545
3704
  this.legend = legend;
3546
3705
  return $('fieldset legend', this.el).first().html(this.legend);
3706
+ },
3707
+ flash: function(message) {
3708
+ if (this.$('.toolbar-container.top').length > 0) {
3709
+ return this.$('.toolbar-container.top').after(message);
3710
+ } else {
3711
+ return this.$bodyEl().prepend(message);
3712
+ }
3713
+ },
3714
+ successFlashDelay: 1500,
3715
+ successMessage: function(message) {
3716
+ var _this = this;
3717
+ this.$('.alert.alert-success').remove();
3718
+ this.flash(Luca.template("components/form_alert", {
3719
+ className: "alert alert-success",
3720
+ message: message
3721
+ }));
3722
+ return _.delay(function() {
3723
+ return _this.$('.alert.alert-success').fadeOut();
3724
+ }, this.successFlashDelay || 0);
3725
+ },
3726
+ errorMessage: function(message) {
3727
+ this.$('.alert.alert-error').remove();
3728
+ return this.flash(Luca.template("components/form_alert", {
3729
+ className: "alert alert-error",
3730
+ message: message
3731
+ }));
3547
3732
  }
3548
3733
  });
3549
3734
 
@@ -3581,12 +3766,10 @@
3581
3766
  Luca.components.Panel.prototype.initialize.apply(this, arguments);
3582
3767
  this.configure_collection(true);
3583
3768
  this.collection.bind("before:fetch", function() {
3584
- console.log("Triggering Enable Load Mask");
3585
3769
  if (_this.loadMask === true) return _this.trigger("enable:loadmask");
3586
3770
  });
3587
3771
  this.collection.bind("reset", function(collection) {
3588
3772
  _this.refresh();
3589
- console.log("Triggering Disable LoadMask");
3590
3773
  if (_this.loadMask === true) _this.trigger("disable:loadmask");
3591
3774
  return _this.trigger("after:collection:load", collection);
3592
3775
  });
@@ -3598,9 +3781,9 @@
3598
3781
  cells = _this.render_row(model, _this.collection.indexOf(model), {
3599
3782
  cellsOnly: true
3600
3783
  });
3601
- return $(rowEl).html(cells);
3784
+ return $(rowEl).html(cells.join(" "));
3602
3785
  } catch (error) {
3603
- return console.log("Error in change handler for GridView.collection", error, _this, model, rowEl, cells);
3786
+ return console.log("Error in change handler for GridView.collection", error, _this, model);
3604
3787
  }
3605
3788
  });
3606
3789
  },
@@ -3806,17 +3989,16 @@
3806
3989
  fixed: true,
3807
3990
  position: 'top',
3808
3991
  className: 'navbar',
3809
- initialize: function(options) {
3810
- this.options = options != null ? options : {};
3811
- return Luca.View.prototype.initialize.apply(this, arguments);
3812
- },
3813
3992
  brand: "Luca.js",
3814
3993
  bodyTemplate: 'nav_bar',
3815
3994
  bodyClassName: 'luca-ui-navbar-body',
3816
3995
  beforeRender: function() {
3817
3996
  if (this.fixed) this.$el.addClass("navbar-fixed-" + this.position);
3818
3997
  if (this.brand != null) {
3819
- return this.content().append("<a class='brand' href='#'>" + this.brand + "</a>");
3998
+ this.content().append("<a class='brand' href='#'>" + this.brand + "</a>");
3999
+ }
4000
+ if (this.template) {
4001
+ return this.content().append(Luca.template(this.template, this));
3820
4002
  }
3821
4003
  },
3822
4004
  render: function() {