stormfront-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/lib/stormfront/rails/version.rb +5 -0
- data/lib/stormfront/rails.rb +7 -0
- data/stormfront-rails.gemspec +22 -0
- data/vendor/assets/javascripts/stormfront/Alert.js +17 -0
- data/vendor/assets/javascripts/stormfront/Container.js +31 -0
- data/vendor/assets/javascripts/stormfront/Entity.js +53 -0
- data/vendor/assets/javascripts/stormfront/Errors.js +25 -0
- data/vendor/assets/javascripts/stormfront/KeyboardEvents.js +52 -0
- data/vendor/assets/javascripts/stormfront/Layout.js +60 -0
- data/vendor/assets/javascripts/stormfront/List.js +123 -0
- data/vendor/assets/javascripts/stormfront/MouseEvents.js +146 -0
- data/vendor/assets/javascripts/stormfront/Namespaces.js +5 -0
- data/vendor/assets/javascripts/stormfront/Overlay.js +178 -0
- data/vendor/assets/javascripts/stormfront/Reducer.js +9 -0
- data/vendor/assets/javascripts/stormfront/Request.js +39 -0
- data/vendor/assets/javascripts/stormfront/View.js +55 -0
- data/vendor/assets/javascripts/stormfront/ViewBase.js +57 -0
- data/vendor/assets/javascripts/stormfront/mixin/Dispatch.js +19 -0
- data/vendor/assets/javascripts/stormfront/mixin/Other.js +25 -0
- data/vendor/assets/javascripts/stormfront/support/Chaperone.js +69 -0
- data/vendor/assets/javascripts/stormfront/support/Template.js +75 -0
- data/vendor/assets/javascripts/stormfront/types/Arguments.js +27 -0
- data/vendor/assets/javascripts/stormfront/types/Class.js +25 -0
- data/vendor/assets/javascripts/stormfront/types/Hash.js +23 -0
- data/vendor/assets/javascripts/stormfront/types/Number.js +15 -0
- data/vendor/assets/javascripts/stormfront/types/Response.js +31 -0
- data/vendor/assets/javascripts/stormfront/types/String.js +44 -0
- data/vendor/assets/javascripts/stormfront/types/Time.js +29 -0
- data/vendor/assets/javascripts/stormfront.js +33 -0
- metadata +148 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
Stormfront.Overlay = Stormfront.Class.extend({
|
2
|
+
initialize: function(options){
|
3
|
+
var selector = options.selector;
|
4
|
+
|
5
|
+
if (!selector)
|
6
|
+
this.overlay = new Stormfront.FullScreenOverlay({model: options});
|
7
|
+
else if (options.saveRequest)
|
8
|
+
this.overlay = new Stormfront.InlineOverlay();
|
9
|
+
else
|
10
|
+
this.overlay = new Stormfront.AnonymousOverlay();
|
11
|
+
|
12
|
+
selector = selector ? selector : document.body;
|
13
|
+
this.location = selector.size ? selector : $(selector);
|
14
|
+
this.render();
|
15
|
+
},
|
16
|
+
render: function(){
|
17
|
+
this.location.append(this.overlay.render().$el);
|
18
|
+
this.overlay.transition('attached');
|
19
|
+
return this;
|
20
|
+
},
|
21
|
+
close: function(){
|
22
|
+
this.overlay.close();
|
23
|
+
this.location = null;
|
24
|
+
},
|
25
|
+
error: function(){
|
26
|
+
this.overlay.error.apply(this.overlay, arguments);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
|
30
|
+
Stormfront.OverlayBase = Stormfront.View.extend({
|
31
|
+
className: 'overlay',
|
32
|
+
activated: false,
|
33
|
+
PAUSE_DURATION: 0,
|
34
|
+
MINIMUM_DURATION: 0,
|
35
|
+
base_css: {
|
36
|
+
overlay_background: {
|
37
|
+
top: '0px', left: '0px',
|
38
|
+
width: '100%', height: '100%'
|
39
|
+
},
|
40
|
+
overlay_panel: {
|
41
|
+
position: 'absolute',
|
42
|
+
top: '50%', left: '50%'
|
43
|
+
}
|
44
|
+
},
|
45
|
+
base: {
|
46
|
+
rendering: function(){
|
47
|
+
function applyCss(value, key){
|
48
|
+
var selector = '.' + key;
|
49
|
+
this.$(selector).css(value);
|
50
|
+
}
|
51
|
+
this.startTime = stormfront.time().now();
|
52
|
+
_.each(this.base_css, applyCss, this);
|
53
|
+
_.each(this.css, applyCss, this);
|
54
|
+
this.$el.css({opacity: '0', 'pointer-events': 'none'});
|
55
|
+
},
|
56
|
+
attached: function(){
|
57
|
+
var self = this;
|
58
|
+
function activate(){
|
59
|
+
self.$el.css({opacity: '1', 'pointer-events': 'auto'});
|
60
|
+
self.activated = true;
|
61
|
+
}
|
62
|
+
function setDimensions(panel){
|
63
|
+
var content = self.$('.content');
|
64
|
+
var position = {
|
65
|
+
'margin-left': - content.outerWidth() / 2,
|
66
|
+
'margin-top': - content.outerHeight() / 2
|
67
|
+
};
|
68
|
+
panel.css(position);
|
69
|
+
}
|
70
|
+
setDimensions(this.$('.overlay_panel'));
|
71
|
+
if (this.PAUSE_DURATION)
|
72
|
+
setTimeout(activate, this.PAUSE_DURATION);
|
73
|
+
else
|
74
|
+
activate();
|
75
|
+
},
|
76
|
+
closing: function(){
|
77
|
+
var self = this;
|
78
|
+
function remove(){
|
79
|
+
self.remove();
|
80
|
+
}
|
81
|
+
if (!this.activated)
|
82
|
+
this.remove();
|
83
|
+
|
84
|
+
var currentDuration = stormfront.time().now() - this.startTime;
|
85
|
+
var remainingDuration = this.MINIMUM_DURATION - currentDuration;
|
86
|
+
if (remainingDuration > 0)
|
87
|
+
setTimeout(remove, remainingDuration);
|
88
|
+
else
|
89
|
+
this.remove();
|
90
|
+
}
|
91
|
+
},
|
92
|
+
initialize: function(){
|
93
|
+
this.addSubscriber(this.base);
|
94
|
+
Stormfront.View.prototype.initialize.apply(this, arguments);
|
95
|
+
},
|
96
|
+
error: function(){
|
97
|
+
this.remove();
|
98
|
+
},
|
99
|
+
close: function(){
|
100
|
+
this.transition.apply(this, stormfront.arguments(arguments).prepend('closing').get());
|
101
|
+
}
|
102
|
+
});
|
103
|
+
|
104
|
+
Stormfront.FullScreenOverlay = Stormfront.OverlayBase.extend({
|
105
|
+
MINIMUM_DURATION: 900,
|
106
|
+
css: {
|
107
|
+
overlay_background: {
|
108
|
+
position: 'fixed',
|
109
|
+
'z-index': '100000'
|
110
|
+
},
|
111
|
+
overlay_panel: {
|
112
|
+
'z-index': '100001'
|
113
|
+
}
|
114
|
+
},
|
115
|
+
when: {
|
116
|
+
rendering: function(){
|
117
|
+
this.$el.addClass('blocking');
|
118
|
+
}
|
119
|
+
}
|
120
|
+
});
|
121
|
+
|
122
|
+
Stormfront.AnonymousOverlay = Stormfront.OverlayBase.extend({
|
123
|
+
PAUSE_DURATION: 200,
|
124
|
+
MINIMUM_DURATION: 450,
|
125
|
+
css: {
|
126
|
+
overlay_background: {
|
127
|
+
position: 'absolute',
|
128
|
+
'z-index': 'auto'
|
129
|
+
},
|
130
|
+
overlay_panel: {
|
131
|
+
'z-index': 'auto'
|
132
|
+
}
|
133
|
+
},
|
134
|
+
when: {
|
135
|
+
rendering: function(){
|
136
|
+
this.$el.addClass('anonymous');
|
137
|
+
}
|
138
|
+
}
|
139
|
+
});
|
140
|
+
|
141
|
+
Stormfront.InlineOverlay = Stormfront.OverlayBase.extend({
|
142
|
+
className: 'input_overlay',
|
143
|
+
MINIMUM_DURATION: 1500,
|
144
|
+
MAXIMUM_WIDTH: 1200,
|
145
|
+
base_css: {
|
146
|
+
input: {
|
147
|
+
position: 'absolute',
|
148
|
+
top: '2px',
|
149
|
+
right: '10px',
|
150
|
+
width:'16px',
|
151
|
+
'z-index':'10000'
|
152
|
+
}
|
153
|
+
},
|
154
|
+
when: {
|
155
|
+
rendering: function () {
|
156
|
+
this.$el.find('.error').hide();
|
157
|
+
this.$el.find('.confirm').hide();
|
158
|
+
},
|
159
|
+
closing: function(){
|
160
|
+
this.$el.find('.spinner').hide();
|
161
|
+
this.$el.find('.confirm').show();
|
162
|
+
}
|
163
|
+
},
|
164
|
+
error: function (xhr) {
|
165
|
+
//TODO: Elevate this code up a level to the Request
|
166
|
+
try {
|
167
|
+
var response = JSON.parse(xhr.responseText);
|
168
|
+
var error = response.error || response.message;
|
169
|
+
this.$el.find('.spinner').hide();
|
170
|
+
this.$el.find('.error').show().attr('title', error);
|
171
|
+
xhr.handled = true;
|
172
|
+
}
|
173
|
+
catch (e) {
|
174
|
+
console.warn('Could not parse error', arguments);
|
175
|
+
this.remove();
|
176
|
+
}
|
177
|
+
}
|
178
|
+
});
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Stormfront.Request = function(model, request, options){
|
2
|
+
var startTime = stormfront.time.now();
|
3
|
+
var overlay = new Stormfront.Overlay(options);
|
4
|
+
function tryRequest(){
|
5
|
+
return shouldWait() ? delayRequest() : sendRequest();
|
6
|
+
}
|
7
|
+
function shouldWait(){
|
8
|
+
//TODO: This is a dependency upon the Indigo behavior
|
9
|
+
return options.wait && $.xhrPool.length > 0;
|
10
|
+
}
|
11
|
+
function delayRequest(){
|
12
|
+
if (stormfront.time.now() - startTime < 5000)
|
13
|
+
setTimeout(tryRequest, 1000);
|
14
|
+
else
|
15
|
+
overlay.error('Request timed out. Please try again soon.');
|
16
|
+
return null;
|
17
|
+
}
|
18
|
+
function sendRequest(){
|
19
|
+
function onComplete(){
|
20
|
+
removeListeners();
|
21
|
+
overlay.close();
|
22
|
+
}
|
23
|
+
function onError(model, xhr){
|
24
|
+
removeListeners();
|
25
|
+
overlay.error(xhr);
|
26
|
+
}
|
27
|
+
function removeListeners(){
|
28
|
+
model.off('error', onError);
|
29
|
+
model.off('sync', onComplete);
|
30
|
+
model.off('invalid', onComplete);
|
31
|
+
}
|
32
|
+
model.once('invalid', onError);
|
33
|
+
model.once('error', onError);
|
34
|
+
model.once('sync', onComplete);
|
35
|
+
|
36
|
+
return request.call(model, options.args, options.options);
|
37
|
+
}
|
38
|
+
return tryRequest();
|
39
|
+
};
|
@@ -0,0 +1,55 @@
|
|
1
|
+
Stormfront.View = Stormfront.ViewBase.extend({
|
2
|
+
initialize: function(options){
|
3
|
+
Stormfront.ViewBase.prototype.initialize.call(this, options);
|
4
|
+
this.when = this.when || { };
|
5
|
+
this.addPublisher(this);
|
6
|
+
this.addSubscriber(this.when);
|
7
|
+
this.transition.apply(this, stormfront.arguments(arguments).prepend('initializing').get());
|
8
|
+
},
|
9
|
+
render: function(){
|
10
|
+
Stormfront.ViewBase.prototype.render.call(this);
|
11
|
+
this.transition.apply(this, stormfront.arguments(arguments).prepend('rendering').get());
|
12
|
+
return this;
|
13
|
+
},
|
14
|
+
addPublisher: function(publisher, identity){
|
15
|
+
function notify(event){
|
16
|
+
var args = stormfront.arguments(arguments).skip(1).get();
|
17
|
+
function executeHandler(subscriber){
|
18
|
+
var handler = stormfront.hash(subscriber).findNestedValue(event.split(':'));
|
19
|
+
if (_.isFunction(handler))
|
20
|
+
handler.apply(this, args);
|
21
|
+
}
|
22
|
+
_.each(this._subscribers, executeHandler, this);
|
23
|
+
}
|
24
|
+
function prepend(identity){
|
25
|
+
return function(event){
|
26
|
+
var newEvent = identity + ':' + event;
|
27
|
+
var args = stormfront.arguments(arguments).skip(1).prepend(newEvent).get();
|
28
|
+
notify.apply(this, args);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
if (publisher === this)
|
32
|
+
this.listenTo(publisher, 'transition', notify);
|
33
|
+
else if (this.model && publisher === this.model)
|
34
|
+
this.listenTo(publisher, 'all', prepend('model'));
|
35
|
+
else if (this.collection && publisher === this.collection)
|
36
|
+
this.listenTo(publisher, 'all', prepend('collection'));
|
37
|
+
else if (publisher.className)
|
38
|
+
this.listenTo(publisher, 'transition', prepend(publisher.getIdentity()));
|
39
|
+
else
|
40
|
+
this.listenTo(publisher, 'all', prepend(identity));
|
41
|
+
},
|
42
|
+
addSubscriber: function(whenBlock){
|
43
|
+
this._subscribers = this._subscribers || [];
|
44
|
+
if (whenBlock)
|
45
|
+
this._subscribers.push(whenBlock);
|
46
|
+
},
|
47
|
+
transition: function(){
|
48
|
+
var args = stormfront.arguments(arguments).prepend('transition').get();
|
49
|
+
this.trigger.apply(this, args);
|
50
|
+
},
|
51
|
+
close: function(){
|
52
|
+
this.transition.apply(this, stormfront.arguments(arguments).prepend('closing').get());
|
53
|
+
Stormfront.ViewBase.prototype.close.call(this);
|
54
|
+
}
|
55
|
+
});
|
@@ -0,0 +1,57 @@
|
|
1
|
+
Stormfront.ViewBase = Backbone.View.extend({
|
2
|
+
initialize: function(){
|
3
|
+
this.errors = [];
|
4
|
+
},
|
5
|
+
render: function(){
|
6
|
+
try {
|
7
|
+
this.$el.html(new Stormfront.Template(this).render());
|
8
|
+
}
|
9
|
+
catch(e) {
|
10
|
+
this.handleException(e);
|
11
|
+
}
|
12
|
+
return this;
|
13
|
+
},
|
14
|
+
getViewModel: function(){
|
15
|
+
return this.model ? this.model.toJSON ? this.model.toJSON() : this.model : {};
|
16
|
+
},
|
17
|
+
handleException: function(exception){
|
18
|
+
if (exception.innerException)
|
19
|
+
console.warn(exception.message, exception.innerException);
|
20
|
+
else
|
21
|
+
console.warn(exception.message);
|
22
|
+
|
23
|
+
this.handleError(exception.message);
|
24
|
+
},
|
25
|
+
handleError: function(message){
|
26
|
+
if (_.contains(this.errors, message))
|
27
|
+
return;
|
28
|
+
this.notifyUser(message);
|
29
|
+
},
|
30
|
+
notifyUser: function(message){
|
31
|
+
this.alert && this.alert.close();
|
32
|
+
|
33
|
+
this.errors.push(message);
|
34
|
+
this.alert = new Stormfront.Alert({
|
35
|
+
model: {
|
36
|
+
summary: Stormfront.Errors.FEEDBACK,
|
37
|
+
details: this.errors.join(', ')
|
38
|
+
}
|
39
|
+
});
|
40
|
+
|
41
|
+
this.listenTo(this.alert, 'closing', this.removeNotification);
|
42
|
+
this.$el.append(this.alert.render().$el);
|
43
|
+
},
|
44
|
+
removeNotification: function(){
|
45
|
+
this.errors = [];
|
46
|
+
this.stopListening(this.alert);
|
47
|
+
},
|
48
|
+
getIdentity: function(){
|
49
|
+
return stormfront.string(this.className).firstWord();
|
50
|
+
},
|
51
|
+
toString: function(){
|
52
|
+
return this.getIdentity();
|
53
|
+
},
|
54
|
+
close: function(){
|
55
|
+
this.remove();
|
56
|
+
}
|
57
|
+
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Stormfront.Mixin.Dispatch = {
|
2
|
+
hasEvents: function(){
|
3
|
+
return this.trigger;
|
4
|
+
},
|
5
|
+
addEvents: function(){
|
6
|
+
$.extend(this, Backbone.Events);
|
7
|
+
},
|
8
|
+
streamlineDispatch: function(event){
|
9
|
+
this.dispatch = function(){
|
10
|
+
this.trigger('dispatch', event)
|
11
|
+
};
|
12
|
+
this.dispatch(event);
|
13
|
+
},
|
14
|
+
dispatch: function(event){
|
15
|
+
if (!this.hasEvents())
|
16
|
+
this.addEvents();
|
17
|
+
this.streamlineDispatch(event);
|
18
|
+
}
|
19
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Stormfront.Mixin.Other = {
|
2
|
+
unauthorized: function(entity){
|
3
|
+
function propagateUnauthorized(source, response, options){
|
4
|
+
if (response.status === 403)
|
5
|
+
this.trigger('unauthorized', source, response, options);
|
6
|
+
}
|
7
|
+
entity.on('error', propagateUnauthorized);
|
8
|
+
},
|
9
|
+
invalid: function(entity){
|
10
|
+
function propagateInvalid(source, response, options){
|
11
|
+
if (response.status === 400)
|
12
|
+
this.trigger('invalid', source, response, options);
|
13
|
+
}
|
14
|
+
entity.on('error', propagateInvalid);
|
15
|
+
},
|
16
|
+
empty: function(collection){
|
17
|
+
function determineEmpty(collection, response, options){
|
18
|
+
if (collection.isEmpty())
|
19
|
+
this.trigger('empty', collection, response, options);
|
20
|
+
else
|
21
|
+
this.trigger('populated', collection, response, options);
|
22
|
+
}
|
23
|
+
collection.on('sync', determineEmpty);
|
24
|
+
}
|
25
|
+
};
|
@@ -0,0 +1,69 @@
|
|
1
|
+
Stormfront.Chaperone = Stormfront.Class.extend({
|
2
|
+
initialize: function(){
|
3
|
+
this.children = {};
|
4
|
+
_.extend(this, Backbone.Events);
|
5
|
+
this.throttle('create', 'created', Stormfront.Errors.INITIALIZING);
|
6
|
+
this.throttle('render', 'rendered', Stormfront.Errors.RENDERING);
|
7
|
+
this.throttle('attach', 'attached', Stormfront.Errors.ATTACHING);
|
8
|
+
this.wrap('add', 'added', Stormfront.Errors.ADDING);
|
9
|
+
this.wrap('remove', 'removed', Stormfront.Errors.CLOSING);
|
10
|
+
},
|
11
|
+
|
12
|
+
create: function(childClass, options, context){
|
13
|
+
return new window[childClass](options.call(context));
|
14
|
+
},
|
15
|
+
find: function(identity){
|
16
|
+
return this.children[identity];
|
17
|
+
},
|
18
|
+
add: function(child){
|
19
|
+
var identity = child.getIdentity();
|
20
|
+
this.remove(this.find(identity));
|
21
|
+
this.children[identity] = child;
|
22
|
+
return child;
|
23
|
+
},
|
24
|
+
remove: function(child){
|
25
|
+
if (child)
|
26
|
+
this.children = _.omit(this.children, child.getIdentity());
|
27
|
+
return child;
|
28
|
+
},
|
29
|
+
render: function(child){
|
30
|
+
return child.render();
|
31
|
+
},
|
32
|
+
attach: function(child, location, method){
|
33
|
+
if (location.size() === 0)
|
34
|
+
throw Stormfront.Errors.NO_LOCATION(child);
|
35
|
+
else if (location.size() > 1)
|
36
|
+
throw Stormfront.Errors.MULTIPLE_LOCATIONS(child);
|
37
|
+
|
38
|
+
location[method](child.$el);
|
39
|
+
return child;
|
40
|
+
},
|
41
|
+
|
42
|
+
wrap: function(field, event, error){
|
43
|
+
var implementation = this[field];
|
44
|
+
this[field] = function(){
|
45
|
+
try {
|
46
|
+
var result = implementation.apply(this, arguments);
|
47
|
+
result && this.trigger(event, result);
|
48
|
+
} catch (e) {
|
49
|
+
this.trigger('error', error(arguments[0].toString()), e);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
},
|
53
|
+
throttle: function(field, event, error){
|
54
|
+
this.wrap(field, event, error);
|
55
|
+
var implementation = this[field];
|
56
|
+
this[field] = function(){
|
57
|
+
var self = this;
|
58
|
+
var capturedArguments = arguments;
|
59
|
+
function func(){
|
60
|
+
implementation.apply(self, capturedArguments);
|
61
|
+
}
|
62
|
+
setTimeout(func, 5);
|
63
|
+
}
|
64
|
+
},
|
65
|
+
|
66
|
+
close: function(){
|
67
|
+
_.each(this.children, this.remove, this);
|
68
|
+
}
|
69
|
+
});
|
@@ -0,0 +1,75 @@
|
|
1
|
+
Stormfront.Template = Stormfront.Class.extend({
|
2
|
+
initialize: function(view){
|
3
|
+
this.view = view;
|
4
|
+
},
|
5
|
+
render: function(){
|
6
|
+
function renderTemplate(view){
|
7
|
+
function getTemplate(view){
|
8
|
+
var selector = view.getIdentity();
|
9
|
+
return $('#' + selector + '_template');
|
10
|
+
}
|
11
|
+
function compileTemplate(selection){
|
12
|
+
return Handlebars.compile(selection.html());
|
13
|
+
}
|
14
|
+
function expandTemplate(template){
|
15
|
+
return template(view.getViewModel());
|
16
|
+
}
|
17
|
+
return expandTemplate(compileTemplate(getTemplate(view)))
|
18
|
+
}
|
19
|
+
function findError(view){
|
20
|
+
function getTemplate(view){
|
21
|
+
if (!view.className)
|
22
|
+
throw Stormfront.Errors.NO_SELECTOR();
|
23
|
+
var selector = view.getIdentity();
|
24
|
+
return $('#' + selector + '_template');
|
25
|
+
}
|
26
|
+
function compileTemplate(template){
|
27
|
+
if (template.size() === 1)
|
28
|
+
return template;
|
29
|
+
if (template.size() === 0)
|
30
|
+
throw Stormfront.Errors.NO_TEMPLATE(view.getIdentity());
|
31
|
+
|
32
|
+
return Handlebars.compile(template.html());
|
33
|
+
}
|
34
|
+
function expandTemplate(template, vm){
|
35
|
+
try {
|
36
|
+
return template(vm);
|
37
|
+
}
|
38
|
+
catch(e) {
|
39
|
+
throw Stormfront.Errors.INCOMPATIBLE_TEMPLATE(view.getIdentity());
|
40
|
+
}
|
41
|
+
}
|
42
|
+
function getViewModel(view){
|
43
|
+
try {
|
44
|
+
return view.getViewModel();
|
45
|
+
}
|
46
|
+
catch (e){
|
47
|
+
throw Stormfront.Errors.VIEW_MODEL(view.getIdentity());
|
48
|
+
}
|
49
|
+
}
|
50
|
+
try {
|
51
|
+
return expandTemplate(compileTemplate(getTemplate(view)), getViewModel(view));
|
52
|
+
}
|
53
|
+
catch(e) {
|
54
|
+
return e;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
try {
|
58
|
+
return renderTemplate(this.view);
|
59
|
+
}
|
60
|
+
catch(e) {
|
61
|
+
throw new TemplateError(findError(this.view), e.message);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
});
|
65
|
+
|
66
|
+
function TemplateError(message, innerException) {
|
67
|
+
var error = Error.call(this, message);
|
68
|
+
this.name = 'TemplateError';
|
69
|
+
this.message = error.message;
|
70
|
+
this.stack = error.stack;
|
71
|
+
this.innerException = innerException;
|
72
|
+
}
|
73
|
+
|
74
|
+
TemplateError.prototype = Object.create(Error.prototype);
|
75
|
+
TemplateError.prototype.constructor = TemplateError;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
stormfront.arguments = function(args){
|
2
|
+
return new Stormfront.Arguments(args);
|
3
|
+
};
|
4
|
+
|
5
|
+
Stormfront.Arguments = Stormfront.Class.extend({
|
6
|
+
initialize: function(array){
|
7
|
+
if (array)
|
8
|
+
this.array = [].slice.call(array);
|
9
|
+
else
|
10
|
+
this.array = [];
|
11
|
+
},
|
12
|
+
skip: function(start) {
|
13
|
+
this.array = this.array.slice(start);
|
14
|
+
return this;
|
15
|
+
},
|
16
|
+
prepend: function(item){
|
17
|
+
this.array.unshift(item);
|
18
|
+
return this;
|
19
|
+
},
|
20
|
+
at: function(index, value){
|
21
|
+
this.array[index] = value;
|
22
|
+
return this;
|
23
|
+
},
|
24
|
+
get: function(){
|
25
|
+
return this.array;
|
26
|
+
}
|
27
|
+
});
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Stormfront.Class = function(options) {
|
2
|
+
if (this.initialize)
|
3
|
+
this.initialize.apply(this, arguments);
|
4
|
+
};
|
5
|
+
|
6
|
+
Stormfront.Class.extend = function(protoProps, staticProps) {
|
7
|
+
var parent = this;
|
8
|
+
var child;
|
9
|
+
|
10
|
+
if (protoProps && _.has(protoProps, 'constructor'))
|
11
|
+
child = protoProps.constructor;
|
12
|
+
else
|
13
|
+
child = function(){ return parent.apply(this, arguments); };
|
14
|
+
|
15
|
+
_.extend(child, parent, staticProps);
|
16
|
+
|
17
|
+
var Surrogate = function(){ this.constructor = child; };
|
18
|
+
Surrogate.prototype = parent.prototype;
|
19
|
+
child.prototype = new Surrogate;
|
20
|
+
|
21
|
+
if (protoProps) _.extend(child.prototype, protoProps);
|
22
|
+
child.__super__ = parent.prototype;
|
23
|
+
|
24
|
+
return child;
|
25
|
+
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
stormfront.hash = function(hash){
|
2
|
+
return new Stormfront.Hash(hash);
|
3
|
+
};
|
4
|
+
|
5
|
+
Stormfront.Hash = Stormfront.Class.extend({
|
6
|
+
initialize: function(hash){
|
7
|
+
this.hash = hash;
|
8
|
+
},
|
9
|
+
findNestedValue: function(keys){
|
10
|
+
function retrieve(keys, context){
|
11
|
+
if (!context)
|
12
|
+
return null;
|
13
|
+
if (keys.length === 0)
|
14
|
+
return context;
|
15
|
+
var current = keys.shift();
|
16
|
+
return retrieve(keys, context[current]);
|
17
|
+
}
|
18
|
+
return retrieve(keys, this.hash);
|
19
|
+
},
|
20
|
+
subtract: function(values){
|
21
|
+
return _.omit(this.hash, _.keys(values));
|
22
|
+
}
|
23
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
stormfront.number = function(number){
|
2
|
+
return new Stormfront.Number(number);
|
3
|
+
};
|
4
|
+
|
5
|
+
Stormfront.Number = Stormfront.Class.extend({
|
6
|
+
initialize: function(number){
|
7
|
+
this.number = number;
|
8
|
+
},
|
9
|
+
isWithin: function(range){
|
10
|
+
return range[0] < this.number && range[1] > this.number;
|
11
|
+
},
|
12
|
+
compare: function(value){
|
13
|
+
return this.number < value ? -1 : this.number > value ? 1 : 0;
|
14
|
+
}
|
15
|
+
});
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Stormfront.Response = Stormfront.Class.extend({
|
2
|
+
initialize: function(xhr){
|
3
|
+
this.xhr = xhr;
|
4
|
+
},
|
5
|
+
isActionable: function(){
|
6
|
+
return !(this.hasBeenHandled() || this.isUserUnauthorized() || this.hasBeenAborted());
|
7
|
+
},
|
8
|
+
hasBeenHandled: function(){
|
9
|
+
return this.xhr.handled === true;
|
10
|
+
},
|
11
|
+
markAsHandled: function(){
|
12
|
+
this.xhr.handled = true;
|
13
|
+
},
|
14
|
+
isUserUnauthorized: function(){
|
15
|
+
return this.xhr.status === 401;
|
16
|
+
},
|
17
|
+
hasBeenAborted: function(){
|
18
|
+
//TODO: consider xhr.status === 'abort'?
|
19
|
+
return this.xhr.readyState != 4;
|
20
|
+
},
|
21
|
+
getError: function(){
|
22
|
+
try {
|
23
|
+
var message = JSON.parse(this.xhr.responseText);
|
24
|
+
return message.error || message.message;
|
25
|
+
}
|
26
|
+
catch (e){
|
27
|
+
console.warn('Unhandled error', this.xhr, e);
|
28
|
+
return Stormfront.Errors.FEEDBACK;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
});
|