kea-rails 1.0.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/kea/bindings/activity.js +24 -0
- data/app/assets/javascripts/kea/bindings/child_vm.js +32 -0
- data/app/assets/javascripts/kea/bindings/komplete.js +172 -0
- data/app/assets/javascripts/kea/bindings/sherlock.js +47 -0
- data/app/assets/javascripts/kea/bindings/sherlock_provider_search.js +57 -0
- data/app/assets/javascripts/kea/bindings/submit_button.js +33 -0
- data/app/assets/javascripts/kea/bindings/validation_state.js +38 -0
- data/app/assets/javascripts/kea/bindings/wait_for_vm.js +44 -0
- data/app/assets/javascripts/kea/extenders/equals.js +85 -0
- data/app/assets/javascripts/kea/extenders/external_validator.js +65 -0
- data/app/assets/javascripts/kea/extenders/min_length.js +39 -0
- data/app/assets/javascripts/kea/extenders/numeric_range.js +64 -0
- data/app/assets/javascripts/kea/extenders/required.js +39 -0
- data/app/assets/javascripts/kea/extenders/valid_date.js +32 -0
- data/app/assets/javascripts/kea/extenders/valid_time.js +41 -0
- data/app/assets/javascripts/kea/extenders/validator_base.js +75 -0
- data/app/assets/javascripts/kea/helpers/kea.activity_button.js +97 -0
- data/app/assets/javascripts/kea/helpers/kea.notify.js +46 -0
- data/app/assets/javascripts/kea/initializers/base.js +11 -0
- data/app/assets/javascripts/kea/kea.js +14 -0
- data/app/assets/javascripts/kea/kea_dependencies.js +20 -0
- data/app/assets/javascripts/kea/kea_init.js +24 -0
- data/app/assets/javascripts/kea/models/base.js +215 -0
- data/app/assets/javascripts/kea/overlay/child_vm_overlay.js +49 -0
- data/app/assets/javascripts/kea/overlay/overlay_control.js +87 -0
- data/app/assets/javascripts/kea/overlay/overlay_template.js +58 -0
- data/app/assets/javascripts/kea/services/base.js +227 -0
- data/app/assets/javascripts/kea/sherlock/base_provider.js +201 -0
- data/app/assets/javascripts/kea/viewmodels/base.js +150 -0
- data/app/assets/javascripts/kea/viewmodels/sherlock.js +230 -0
- data/app/assets/stylesheets/kea/kea.css.sass +16 -0
- data/app/helpers/kea/application_helper.rb +42 -0
- data/lib/generators/kea/install/USAGE +9 -0
- data/lib/generators/kea/install/install_generator.rb +197 -0
- data/lib/generators/kea/install/templates/_komplete.sass +17 -0
- data/lib/generators/kea/install/templates/_sherlock.sass +105 -0
- data/lib/generators/kea/install/templates/application.html.erb +38 -0
- data/lib/generators/kea/install/templates/global.js +7 -0
- data/lib/generators/kea/install/templates/init.js +18 -0
- data/lib/generators/kea/install/templates/main.js +18 -0
- data/lib/generators/kea/model/USAGE +7 -0
- data/lib/generators/kea/model/model_generator.rb +68 -0
- data/lib/generators/kea/model/templates/model.js.erb +96 -0
- data/lib/generators/kea/service/USAGE +7 -0
- data/lib/generators/kea/service/service_generator.rb +21 -0
- data/lib/generators/kea/service/templates/model.js.erb +58 -0
- data/lib/generators/kea/viewmodel/USAGE +6 -0
- data/lib/generators/kea/viewmodel/templates/viewmodel.js.erb +73 -0
- data/lib/generators/kea/viewmodel/viewmodel_generator.rb +21 -0
- data/lib/kea-rails/engine.rb +5 -0
- data/lib/kea-rails/version.rb +3 -0
- data/lib/kea-rails.rb +4 -0
- data/lib/tasks/kea_tasks.rake +4 -0
- metadata +115 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
(function(app, sherlock, ko, Fuse) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
var Base;
|
5
|
+
|
6
|
+
Base = function Base() {
|
7
|
+
var that = this;
|
8
|
+
|
9
|
+
this.minimumConfidence = 0.8;
|
10
|
+
this.displayName = '';
|
11
|
+
this.defaultKeyword = '';
|
12
|
+
this.parameterName = '';
|
13
|
+
this.keywords = [];
|
14
|
+
this.activeFragments = ko.observableArray([]);
|
15
|
+
this.prefilledFragmentsAreChangeable = true;
|
16
|
+
|
17
|
+
this.liveSearchFragments = ko.observableArray([]);
|
18
|
+
this.liveSearchAjaxActivity = ko.observable(false);
|
19
|
+
this.liveSearchAlwaysVisible = ko.observable(false);
|
20
|
+
|
21
|
+
this.liveSearchVisible = ko.computed(function() {
|
22
|
+
return this.liveSearchAlwaysVisible() || this.liveSearchFragments().length > 0;
|
23
|
+
}, this, {deferEvaluation: true});
|
24
|
+
|
25
|
+
this.offersLiveSearch = ko.computed(function() {
|
26
|
+
return typeof this.liveSearch === 'function' && this.allowAnotherFragment();
|
27
|
+
}, this, {deferEvaluation: true});
|
28
|
+
|
29
|
+
this.fragmentsToParam = ko.computed(function() {
|
30
|
+
var result = {},
|
31
|
+
values = [];
|
32
|
+
|
33
|
+
this.activeFragments().forEach(function(activeFragment) {
|
34
|
+
var fragmentResult = activeFragment.toParam();
|
35
|
+
|
36
|
+
if (!fragmentResult) {
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
|
40
|
+
values.push(fragmentResult);
|
41
|
+
});
|
42
|
+
|
43
|
+
if (values.length === 0) {
|
44
|
+
return;
|
45
|
+
|
46
|
+
} else if (values.length === 1) {
|
47
|
+
result[this.parameterName] = values[0];
|
48
|
+
|
49
|
+
} else {
|
50
|
+
result[this.parameterName] = values;
|
51
|
+
}
|
52
|
+
|
53
|
+
return result;
|
54
|
+
|
55
|
+
}, this, {deferEvaluation: true});
|
56
|
+
};
|
57
|
+
|
58
|
+
Base.prototype.allowAnotherFragment = function allowAnotherFragment() {
|
59
|
+
return this.activeFragments().length === 0;
|
60
|
+
};
|
61
|
+
|
62
|
+
Base.prototype.setStaticKeywords = function setStaticKeywords(stringOrArray) {
|
63
|
+
var that = this,
|
64
|
+
keywords,
|
65
|
+
defaultKeyword;
|
66
|
+
|
67
|
+
if (typeof stringOrArray === 'string') {
|
68
|
+
keywords = [stringOrArray];
|
69
|
+
} else {
|
70
|
+
keywords = stringOrArray;
|
71
|
+
}
|
72
|
+
|
73
|
+
this.keywords.push( {keyword: keywords.shift(), isDefaultKeyword: true} );
|
74
|
+
|
75
|
+
keywords.forEach(function(keyword) {
|
76
|
+
that.keywords.push( {keyword: keyword} );
|
77
|
+
});
|
78
|
+
};
|
79
|
+
|
80
|
+
Base.prototype.respondsTo = function respondsTo(searchTerm) {
|
81
|
+
if (this.offersLiveSearch()) {
|
82
|
+
this.liveSearch(searchTerm);
|
83
|
+
}
|
84
|
+
return this.fuzzyKeywordSearch(searchTerm);
|
85
|
+
};
|
86
|
+
|
87
|
+
Base.prototype.fuzzyKeywordSearch = function fuzzyKeywordSearch(searchTerm) {
|
88
|
+
var that = this,
|
89
|
+
fuse_results = new Fuse(that.keywords, {
|
90
|
+
keys: ['keyword'],
|
91
|
+
includeScore: true,
|
92
|
+
threshold: (1 - this.minimumConfidence)
|
93
|
+
}).search(searchTerm),
|
94
|
+
matches;
|
95
|
+
|
96
|
+
if (!fuse_results) {
|
97
|
+
return;
|
98
|
+
}
|
99
|
+
|
100
|
+
matches = fuse_results.map(function(fuse_result) { return new that.KeywordMatch(fuse_result); });
|
101
|
+
|
102
|
+
matches = matches.map(function(match) {
|
103
|
+
return that.fragmentForMatch(match);
|
104
|
+
});
|
105
|
+
|
106
|
+
return matches.length > 0 ? matches : null;
|
107
|
+
};
|
108
|
+
|
109
|
+
Base.prototype.fragmentForMatch = function fragmentForMatch(match) {
|
110
|
+
var fragment = new this.Fragment(this);
|
111
|
+
|
112
|
+
fragment.importMatch(match);
|
113
|
+
|
114
|
+
if (typeof this.fragmentDisplayStringForMatch === 'function') {
|
115
|
+
fragment.displayString = this.fragmentDisplayStringForMatch(match);
|
116
|
+
|
117
|
+
} else {
|
118
|
+
if (match.isDefaultKeyword) {
|
119
|
+
fragment.displayString = '<span class="keyword default-keyword">' + match.keyword + '</span>';
|
120
|
+
} else {
|
121
|
+
fragment.displayString = '<span class="keyword">' + match.keyword + '</span> — <span class="keyword default-keyword">' + this.displayName + '</span>';
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
return fragment;
|
126
|
+
};
|
127
|
+
|
128
|
+
Base.prototype.defaultFragment = function defaultFragment() {
|
129
|
+
return new this.Fragment(this, this.displayName, 1);
|
130
|
+
};
|
131
|
+
|
132
|
+
Base.prototype.KeywordMatch = function KeywordMatch(fuse_result) {
|
133
|
+
this.isDefaultKeyword = fuse_result.item.isDefaultKeyword || false;
|
134
|
+
this.item = fuse_result.item;
|
135
|
+
this.keyword = fuse_result.item.keyword;
|
136
|
+
this.searchValue = fuse_result.item.searchValue;
|
137
|
+
this.predicate = fuse_result.item.predicate;
|
138
|
+
this.confidence = 1 - fuse_result.score;
|
139
|
+
};
|
140
|
+
|
141
|
+
Base.prototype.Fragment = function BaseFragment(provider, displayString, confidence, searchValue, predicate) {
|
142
|
+
var that = this;
|
143
|
+
|
144
|
+
this.provider = provider;
|
145
|
+
|
146
|
+
this.displayString = displayString;
|
147
|
+
this.confidence = confidence;
|
148
|
+
this.searchValueDisplay = displayString;
|
149
|
+
this.searchValueOptions = ko.observableArray([]);
|
150
|
+
this.predicateOptions = ko.observableArray([]);
|
151
|
+
this.searchValue = ko.observable(searchValue);
|
152
|
+
this.predicate = ko.observable(predicate);
|
153
|
+
this.hasFocus = ko.observable(false);
|
154
|
+
|
155
|
+
if (typeof provider.searchValueOptions === 'function') {
|
156
|
+
this.searchValueOptions( provider.searchValueOptions() );
|
157
|
+
}
|
158
|
+
|
159
|
+
if (typeof provider.predicateOptions === 'function') {
|
160
|
+
this.predicateOptions( provider.predicateOptions() );
|
161
|
+
}
|
162
|
+
|
163
|
+
this.importMatch = function importMatch(match) {
|
164
|
+
if (match.searchValue) {
|
165
|
+
this.searchValue(match.searchValue);
|
166
|
+
}
|
167
|
+
|
168
|
+
if (match.predicate) {
|
169
|
+
this.searchValue(match.predicate);
|
170
|
+
}
|
171
|
+
|
172
|
+
this.confidence = match.confidence;
|
173
|
+
};
|
174
|
+
|
175
|
+
this.changeable = !this.searchValue() || provider.prefilledFragmentsAreChangeable;
|
176
|
+
|
177
|
+
this.toParam = ko.computed(function() {
|
178
|
+
if (!this.searchValue()) {
|
179
|
+
return;
|
180
|
+
}
|
181
|
+
|
182
|
+
if (this.predicate()) {
|
183
|
+
return {
|
184
|
+
predicate: this.predicate(),
|
185
|
+
value: this.searchValue()
|
186
|
+
};
|
187
|
+
|
188
|
+
} else {
|
189
|
+
return this.searchValue();
|
190
|
+
}
|
191
|
+
|
192
|
+
}, this, {deferEvaluation: true});
|
193
|
+
|
194
|
+
};
|
195
|
+
|
196
|
+
Base.prototype.fragment_template_name = 'sherlock-base-fragment-template';
|
197
|
+
Base.prototype.livesearch_template_name = 'sherlock-base-livesearch-template';
|
198
|
+
|
199
|
+
app.sherlock.BaseProvider = Base;
|
200
|
+
|
201
|
+
})(window.app, window.app.sherlock, ko, window.Fuse);
|
@@ -0,0 +1,150 @@
|
|
1
|
+
(function(app, kea, ko) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
// Sub-viewmodel handling. Use with borrowed constructor pattern
|
5
|
+
|
6
|
+
var Parent = function Parent() {
|
7
|
+
var that = this;
|
8
|
+
|
9
|
+
this._childVms = ko.observableArray();
|
10
|
+
|
11
|
+
this.addChildVm = function addChildVm(vm) {
|
12
|
+
var vmName = vm;
|
13
|
+
|
14
|
+
if (DEBUG) {
|
15
|
+
console.assert(app.viewmodels[vmName], vmName + " viewmodel could not be loaded into " + that._viewmodelName());
|
16
|
+
}
|
17
|
+
|
18
|
+
vm = new app.viewmodels[vmName]();
|
19
|
+
|
20
|
+
if (typeof vm.initChildFromParent === 'function') {
|
21
|
+
if (DEBUG) {
|
22
|
+
console.group("%s: init sub-vm %s", that._viewmodelName(), vm._viewmodelName());
|
23
|
+
}
|
24
|
+
|
25
|
+
vm.initChildFromParent(that);
|
26
|
+
|
27
|
+
if (DEBUG) { console.groupEnd(); }
|
28
|
+
}
|
29
|
+
|
30
|
+
that._childVms.push( vm );
|
31
|
+
|
32
|
+
return vm;
|
33
|
+
};
|
34
|
+
|
35
|
+
this.getVm = function getVm(name) {
|
36
|
+
return ko.utils.arrayFirst(that._childVms(), function(item) {
|
37
|
+
return item._viewmodelName() === name;
|
38
|
+
});
|
39
|
+
};
|
40
|
+
};
|
41
|
+
|
42
|
+
kea.viewmodels.Parent = Parent;
|
43
|
+
|
44
|
+
// Parent-viewmodel handling. Use with borrowed constructor pattern
|
45
|
+
|
46
|
+
var Child = function Child() {
|
47
|
+
var that = this;
|
48
|
+
|
49
|
+
this._viewmodelName = function _viewmodelName() {
|
50
|
+
throw "Viewmodel does not implement _viewmodelName";
|
51
|
+
};
|
52
|
+
|
53
|
+
this.parent = null;
|
54
|
+
|
55
|
+
this.initChildFromParent = function initChildFromParent(parentVm) {
|
56
|
+
that.parent = parentVm;
|
57
|
+
|
58
|
+
if (typeof that.init === 'function') {
|
59
|
+
that.init();
|
60
|
+
}
|
61
|
+
};
|
62
|
+
};
|
63
|
+
|
64
|
+
kea.viewmodels.Child = Child;
|
65
|
+
|
66
|
+
// Object validation handling. Use with borrowed constructor pattern
|
67
|
+
|
68
|
+
var Validatable = function Validatable() {
|
69
|
+
var that = this;
|
70
|
+
|
71
|
+
this.validatableName = ko.observable();
|
72
|
+
|
73
|
+
this.validatable = ko.computed(function() {
|
74
|
+
if (typeof that[ that.validatableName() ] !== 'undefined') {
|
75
|
+
return that[ that.validatableName() ]();
|
76
|
+
} else {
|
77
|
+
return undefined;
|
78
|
+
}
|
79
|
+
});
|
80
|
+
|
81
|
+
this.validatableFields = function validatableFields() {
|
82
|
+
if (DEBUG) {
|
83
|
+
console.assert(that.validatable().validatableFields, "%o does not implement validatableFields", that.validatable());
|
84
|
+
}
|
85
|
+
|
86
|
+
return that.validatable().validatableFields();
|
87
|
+
};
|
88
|
+
|
89
|
+
this.hasFieldsWithErrors = ko.computed(function() {
|
90
|
+
var result = false;
|
91
|
+
|
92
|
+
if (!that.validatable()) {
|
93
|
+
return false;
|
94
|
+
}
|
95
|
+
|
96
|
+
ko.utils.arrayForEach(that.validatableFields(), function(field) {
|
97
|
+
if ( that.validatable()[field].hasError() ) {
|
98
|
+
result = true;
|
99
|
+
}
|
100
|
+
});
|
101
|
+
|
102
|
+
return result;
|
103
|
+
});
|
104
|
+
|
105
|
+
this.hasUnvalidatedFields = ko.computed(function() {
|
106
|
+
var result = false;
|
107
|
+
|
108
|
+
if (!that.validatable()) {
|
109
|
+
return false;
|
110
|
+
}
|
111
|
+
|
112
|
+
ko.utils.arrayForEach(that.validatableFields(), function(field) {
|
113
|
+
if ( that.validatable()[field].isUnvalidated() ) {
|
114
|
+
result = true;
|
115
|
+
}
|
116
|
+
});
|
117
|
+
|
118
|
+
return result;
|
119
|
+
});
|
120
|
+
|
121
|
+
this.allFieldsValid = ko.computed(function() {
|
122
|
+
return !that.hasFieldsWithErrors() && !that.hasUnvalidatedFields();
|
123
|
+
});
|
124
|
+
|
125
|
+
this.hasValidationsInProgress = ko.computed(function() {
|
126
|
+
var result = false;
|
127
|
+
|
128
|
+
if (!that.validatable()) {
|
129
|
+
return false;
|
130
|
+
}
|
131
|
+
|
132
|
+
ko.utils.arrayForEach(that.validatableFields(), function(field) {
|
133
|
+
if ( that.validatable()[field].validationInProgress() ) {
|
134
|
+
result = true;
|
135
|
+
}
|
136
|
+
});
|
137
|
+
|
138
|
+
return result;
|
139
|
+
});
|
140
|
+
|
141
|
+
this.validateAllFields = function validateAllFields() {
|
142
|
+
ko.utils.arrayForEach(that.validatableFields(), function(field) {
|
143
|
+
that.validatable()[field].forceValidate();
|
144
|
+
});
|
145
|
+
};
|
146
|
+
};
|
147
|
+
|
148
|
+
kea.viewmodels.Validatable = Validatable;
|
149
|
+
|
150
|
+
})(window.app, window.kea, ko);
|
@@ -0,0 +1,230 @@
|
|
1
|
+
(function(app, ko) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
var SherlockVm;
|
5
|
+
|
6
|
+
SherlockVm = function SherlockVm() {
|
7
|
+
app.viewmodels.Parent.apply(this);
|
8
|
+
app.viewmodels.Child.apply(this);
|
9
|
+
|
10
|
+
var that = this;
|
11
|
+
|
12
|
+
this.providers = ko.observableArray([]);
|
13
|
+
this.providerSearchTerm = ko.observable('').extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
|
14
|
+
this.providerSearchActive = ko.observable(false);
|
15
|
+
this.showLiveSearchWidgets = ko.observable(false);
|
16
|
+
this.activeFragments = ko.observableArray([]);
|
17
|
+
|
18
|
+
this.params = ko.computed(function() {
|
19
|
+
var params = {};
|
20
|
+
|
21
|
+
this.providers().forEach(function(provider) {
|
22
|
+
var param = provider.fragmentsToParam();
|
23
|
+
|
24
|
+
if (param) {
|
25
|
+
params = $.extend({}, params, param);
|
26
|
+
}
|
27
|
+
});
|
28
|
+
|
29
|
+
return params;
|
30
|
+
|
31
|
+
}, this, {deferEvaluation: true});
|
32
|
+
|
33
|
+
this.fragmentsForSearchTerm = ko.observableArray([]);
|
34
|
+
this.orderedFragmentsForSearchTerm = ko.computed(function() {
|
35
|
+
if (!this.fragmentsForSearchTerm()) {
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
|
39
|
+
return this.fragmentsForSearchTerm.sort(function(left, right) {
|
40
|
+
if (left.confidence === right.confidence) {
|
41
|
+
return 0;
|
42
|
+
}
|
43
|
+
|
44
|
+
return left.confidence < right.confidence ? 1 : -1;
|
45
|
+
});
|
46
|
+
|
47
|
+
}, this, {deferEvaluation: true});
|
48
|
+
|
49
|
+
this.availableProviders = ko.computed(function() {
|
50
|
+
var available = [];
|
51
|
+
|
52
|
+
this.providers().forEach(function(provider) {
|
53
|
+
if (provider.allowAnotherFragment()) {
|
54
|
+
available.push(provider);
|
55
|
+
}
|
56
|
+
});
|
57
|
+
|
58
|
+
return available;
|
59
|
+
|
60
|
+
}, this, {deferEvaluation: true});
|
61
|
+
|
62
|
+
this.liveSearchProviders = ko.computed(function() {
|
63
|
+
var available = [];
|
64
|
+
|
65
|
+
this.providers().forEach(function(provider) {
|
66
|
+
if (provider.offersLiveSearch()) {
|
67
|
+
available.push(provider);
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
return available;
|
72
|
+
|
73
|
+
}, this, {deferEvaluation: true});
|
74
|
+
|
75
|
+
this.focuseableFragments = ko.computed(function() {
|
76
|
+
var fragments = [];
|
77
|
+
|
78
|
+
this.orderedFragmentsForSearchTerm().forEach(function(fragment) {
|
79
|
+
fragments.push(fragment);
|
80
|
+
});
|
81
|
+
|
82
|
+
this.liveSearchProviders().forEach(function(provider) {
|
83
|
+
provider.liveSearchFragments().forEach(function(fragment) {
|
84
|
+
fragments.push(fragment);
|
85
|
+
});
|
86
|
+
});
|
87
|
+
|
88
|
+
return fragments;
|
89
|
+
|
90
|
+
}, this, {deferEvaluation: true});
|
91
|
+
|
92
|
+
this.focusNextFragment = function focusNextFragment() {
|
93
|
+
if (that.focuseableFragments().length === 0) {
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
|
97
|
+
var result = that.focuseableFragments().some(function(fragment, idx) {
|
98
|
+
var nextFragment;
|
99
|
+
|
100
|
+
if (!fragment.hasFocus()) {
|
101
|
+
return false;
|
102
|
+
}
|
103
|
+
|
104
|
+
nextFragment = that.focuseableFragments()[idx + 1];
|
105
|
+
|
106
|
+
if (nextFragment) {
|
107
|
+
fragment.hasFocus(false);
|
108
|
+
nextFragment.hasFocus(true);
|
109
|
+
}
|
110
|
+
|
111
|
+
return true;
|
112
|
+
});
|
113
|
+
|
114
|
+
if (!result) {
|
115
|
+
that.focuseableFragments()[0].hasFocus(true);
|
116
|
+
}
|
117
|
+
|
118
|
+
};
|
119
|
+
|
120
|
+
this.focusPreviousFragment = function focusPreviousFragment() {
|
121
|
+
if (that.focuseableFragments().length === 0) {
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
|
125
|
+
var result = that.focuseableFragments().some(function(fragment, idx) {
|
126
|
+
var previousFragment;
|
127
|
+
|
128
|
+
if (!fragment.hasFocus()) {
|
129
|
+
return false;
|
130
|
+
}
|
131
|
+
|
132
|
+
previousFragment = that.focuseableFragments()[idx - 1];
|
133
|
+
|
134
|
+
if (previousFragment) {
|
135
|
+
fragment.hasFocus(false);
|
136
|
+
previousFragment.hasFocus(true);
|
137
|
+
}
|
138
|
+
|
139
|
+
return true;
|
140
|
+
});
|
141
|
+
|
142
|
+
if (!result) {
|
143
|
+
that.focuseableFragments()[0].hasFocus(true);
|
144
|
+
}
|
145
|
+
};
|
146
|
+
|
147
|
+
this.selectFocusedFragment = function selectFocusedFragment() {
|
148
|
+
var fragment = ko.utils.arrayFirst(that.focuseableFragments(), function(fragment) {
|
149
|
+
return fragment.hasFocus();
|
150
|
+
});
|
151
|
+
|
152
|
+
if (fragment) {
|
153
|
+
that.selectFragment(fragment);
|
154
|
+
}
|
155
|
+
};
|
156
|
+
|
157
|
+
this.removeLastActiveFragment = function removeLastActiveFragment() {
|
158
|
+
var fragment = that.activeFragments()[ that.activeFragments().length - 1 ];
|
159
|
+
|
160
|
+
if (fragment) {
|
161
|
+
that.removeFragment(fragment);
|
162
|
+
}
|
163
|
+
};
|
164
|
+
|
165
|
+
this.setDefaultFragments = function setDefaultFragments() {
|
166
|
+
that.availableProviders().forEach(function(provider) {
|
167
|
+
that.fragmentsForSearchTerm.push( provider.defaultFragment() );
|
168
|
+
});
|
169
|
+
};
|
170
|
+
|
171
|
+
this.selectFragment = function selectFragment(fragment) {
|
172
|
+
that.activeFragments.push(fragment);
|
173
|
+
fragment.provider.activeFragments.push(fragment);
|
174
|
+
that.providerSearchActive(false);
|
175
|
+
that.fragmentsForSearchTerm([]);
|
176
|
+
that.providerSearchTerm('');
|
177
|
+
};
|
178
|
+
|
179
|
+
this.removeFragment = function removeFragment(fragment) {
|
180
|
+
fragment.provider.activeFragments.remove(fragment);
|
181
|
+
that.activeFragments.remove(fragment);
|
182
|
+
};
|
183
|
+
|
184
|
+
this.providerSearchTerm.subscribe(function(searchTerm) {
|
185
|
+
var fragments;
|
186
|
+
|
187
|
+
that.fragmentsForSearchTerm([]);
|
188
|
+
|
189
|
+
if (searchTerm.length > 0) {
|
190
|
+
that.showLiveSearchWidgets(true);
|
191
|
+
|
192
|
+
that.availableProviders().forEach(function(provider) {
|
193
|
+
fragments = provider.respondsTo(searchTerm);
|
194
|
+
|
195
|
+
if (fragments) {
|
196
|
+
fragments.forEach(function(fragment) { that.fragmentsForSearchTerm.push(fragment); });
|
197
|
+
fragments[0].hasFocus(true);
|
198
|
+
}
|
199
|
+
});
|
200
|
+
|
201
|
+
} else {
|
202
|
+
if (that.providerSearchActive()) {
|
203
|
+
that.setDefaultFragments();
|
204
|
+
that.showLiveSearchWidgets(false);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
});
|
208
|
+
|
209
|
+
this.providerSearchActive.subscribe(function(isActive) {
|
210
|
+
if (!isActive) {
|
211
|
+
return;
|
212
|
+
}
|
213
|
+
|
214
|
+
if (that.fragmentsForSearchTerm().length === 0) {
|
215
|
+
that.setDefaultFragments();
|
216
|
+
}
|
217
|
+
});
|
218
|
+
|
219
|
+
this.setup = function setup() {
|
220
|
+
for (var providerName in app.sherlock.providers) {
|
221
|
+
if (app.sherlock.providers.hasOwnProperty(providerName)) {
|
222
|
+
that.providers.push( new app.sherlock.providers[providerName]() );
|
223
|
+
}
|
224
|
+
}
|
225
|
+
};
|
226
|
+
};
|
227
|
+
|
228
|
+
app.sherlock.SherlockVm = SherlockVm;
|
229
|
+
|
230
|
+
})(window.app, ko);
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require humane-js/themes/flatty
|
14
|
+
*= require pikaday
|
15
|
+
*= require_self
|
16
|
+
*/
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Kea
|
2
|
+
module ApplicationHelper
|
3
|
+
def body_id
|
4
|
+
content_for?(:body_id) ? content_for(:body_id) : [body_class, params[:action]].join('-')
|
5
|
+
end
|
6
|
+
|
7
|
+
def body_class
|
8
|
+
controller.class.to_s.gsub('Controller', '').underscore.dasherize.gsub('/', '-')
|
9
|
+
end
|
10
|
+
|
11
|
+
def json_for(target, options = {})
|
12
|
+
options[:scope] ||= self
|
13
|
+
options[:url_options] ||= url_options
|
14
|
+
|
15
|
+
target.active_model_serializer.new(target, options).to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_json(object, path = nil, options = {})
|
19
|
+
unless path
|
20
|
+
path = polymorphic_path(object)
|
21
|
+
end
|
22
|
+
|
23
|
+
content = json_for(object, options)
|
24
|
+
|
25
|
+
content_for :json_cache, "window.app.cache['#{path}'] = #{content};\n".html_safe
|
26
|
+
end
|
27
|
+
|
28
|
+
def overlay_template(name, partial: nil, &block)
|
29
|
+
content_for :knockout_templates do
|
30
|
+
if partial
|
31
|
+
content_tag :script, type: "text/html", id: name, "data-bind" => "overlayTemplate: '#{name}'" do
|
32
|
+
render partial: partial
|
33
|
+
end
|
34
|
+
else
|
35
|
+
content_tag :script, type: "text/html", id: name, "data-bind" => "overlayTemplate: '#{name}'" do
|
36
|
+
capture(&block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|