web-connect 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 810c9f86338860a0ced0f39617ccc0a0b9cff27f
4
- data.tar.gz: fdb92385501bde33487bb9ab8b340ce954083cc2
3
+ metadata.gz: a7b6165c1609c61f31bdc2da429040c476f0f7bf
4
+ data.tar.gz: 69f1ff505d4ccf3d6321d891b665f81ac96c4482
5
5
  SHA512:
6
- metadata.gz: 73e97a19149d5dc6e05267a2625cbc5446b7313764280d98e3da3b7591a53f112a6041774fb204689dfc357a887b798aa58473a69f9babc75c8357286c059a59
7
- data.tar.gz: f7a7bc98991f2a17cbffd0ad86e753d8a1c69a37600debe4bf314f9b537cbb2ef113a026e6a5b8f27e10230a8286aed11d9f066a06a39fa64e3eb46a74dad4fb
6
+ metadata.gz: 42793ad4d2d3806fd64c73ec47e31be5c3824bea42dbbec5f35f21150e9a9b231830bf928aa0196c6e383da4589898f85c1f2398b5813b63e176c82021297ecd
7
+ data.tar.gz: 97f707bfb922fd415a4817934ffb8e0a83b703b2b06b432ecba679778a4bf040aeb551972b950c098df0eb8bc13385fde5f3888439be73913d368c76eee09214
@@ -0,0 +1,16 @@
1
+ require 'erb'
2
+
3
+ module Netfira::WebConnect
4
+ class RackApp::Action
5
+
6
+ def send_file(path)
7
+ content = nil
8
+ if path.to_s.end_with? '.erb'
9
+ template = ERB.new(path.read)
10
+ content = template.result(binding)
11
+ end
12
+ raise RackApp::Exceptions::SendFile.new(path, content)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ class Netfira::WebConnect::RackApp
2
+ module Action::Version8
3
+ class Web < Action
4
+
5
+ WEB_BASE = Netfira::WebConnect::ROOT.parent + 'web'
6
+ SEARCH_PATH = [:public, :protected].map { |x| [x, WEB_BASE + x.to_s] }.to_h
7
+
8
+ def call
9
+ raise NotFound unless path
10
+ path = self.path.join '/'
11
+ options = SEARCH_PATH.map do |access, dir|
12
+ ['', '.erb'].map do |extension|
13
+ [access, dir.join(path + extension)]
14
+ end
15
+ end.flatten(1).select { |option| option[1].exist? }
16
+ raise NotFound if options.empty?
17
+ raise InternalServerError, "Ambiguous file name (#{options.length} variations)" unless options.length == 1
18
+ send_file options.first.last
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module Netfira::WebConnect::RackApp::Exceptions
2
+ class SendFile < Base
3
+
4
+ attr_reader :path
5
+
6
+ def initialize(path, content = nil)
7
+ @path = path
8
+ @content = content
9
+ end
10
+
11
+ def content
12
+ @content ||= path.read.force_encoding('ASCII-8BIT')
13
+ end
14
+
15
+ end
16
+ end
@@ -4,3 +4,4 @@ module Netfira::WebConnect::RackApp::Exceptions
4
4
  end
5
5
 
6
6
  require_relative 'exceptions/http_exception'
7
+ require_relative 'exceptions/send_file'
@@ -12,6 +12,15 @@ module Netfira::WebConnect
12
12
  else
13
13
  make_response 200, body, headers
14
14
  end
15
+ rescue Exceptions::SendFile => e
16
+ [
17
+ 200,
18
+ {
19
+ 'Content-Type' => MIME::Types.type_for(e.path.to_s.sub(/\.erb$/, '')).first.content_type,
20
+ 'Content-Length' => e.content.length.to_s
21
+ },
22
+ [e.content]
23
+ ]
15
24
  rescue Exceptions::HttpException => e
16
25
  make_response e.code, e.body, e.headers, e.code
17
26
  end
@@ -1,6 +1,6 @@
1
1
  module Netfira
2
2
  module WebConnect
3
- VERSION = '0.2.2'
3
+ VERSION = '0.3.0'
4
4
  PLATFORM_AND_VERSION = 'Rack/' << VERSION
5
5
  end
6
6
  end
@@ -0,0 +1,4 @@
1
+ (function() {
2
+ this.fileTables = <%= Netfira::WebConnect.schema.select { |model| model.has_file? }.map{ |model| model[0].name.demodulize.pluralize }.to_json %>;
3
+ this.DELETE_KEY = '@delete';
4
+ }).call(this);
Binary file
@@ -0,0 +1,394 @@
1
+ nf.UI = {
2
+ Pager: function(owner) {
3
+ this.owner = owner;
4
+ this.current = 1;
5
+ this.total = 1;
6
+ },
7
+
8
+ Actions: function(owner) {
9
+ this.owner = owner || window;
10
+ this.items = {};
11
+ this.rootElement = null;
12
+ this.searchField = null;
13
+ },
14
+
15
+ Labels: {
16
+ create: 'Create',
17
+ createAll: 'Create All',
18
+ search: 'Search',
19
+ accept: 'Accept',
20
+ unpair: 'Unpair',
21
+ cancel: 'Cancel',
22
+ acceptAll: 'Accept all suggestions',
23
+ goToFirst: String.fromCharCode(0xAB), // looks like <<
24
+ goToLast: String.fromCharCode(0xBB), // looks like >>
25
+ goToPrevious: String.fromCharCode(0x2039), // looks like <
26
+ goToNext: String.fromCharCode(0x203A), // looks like >
27
+ of: 'of'
28
+ },
29
+
30
+ Tests: {
31
+
32
+ email: function(a, b) {
33
+
34
+ // direct comparison (case sensitive)
35
+ if(a == b) {
36
+ return 1;
37
+ }
38
+
39
+ // go case-insensitive
40
+ var x = (a || '').toLowerCase(),
41
+ y = (b || '').toLowerCase();
42
+
43
+ // direct comparision
44
+ if(x == y) {
45
+ return .95;
46
+ }
47
+
48
+ // compare without domain suffixes
49
+ if(x.replace(arguments.callee.variation, '') ==
50
+ y.replace(arguments.callee.variation, '')) {
51
+ return .85;
52
+ }
53
+
54
+ // use deprioritised string comparison
55
+ return nf.UI.Tests.string(a, b) / 3;
56
+ },
57
+
58
+ fullName: function(a, b) {
59
+
60
+ a = (a || '').trim();
61
+ b = (b || '').trim();
62
+
63
+ // direct comparison
64
+
65
+ if(a == b) {
66
+ return 1;
67
+ }
68
+ else if(a.toLowerCase() == b.toLowerCase()) {
69
+ return .9;
70
+ }
71
+
72
+ // go to word by word
73
+
74
+ var p = [a, b].map(function(x) {
75
+ return x
76
+ .trim()
77
+ .split(/[^a-z]+/i)
78
+ .filter(function(y) {
79
+ return y && nf.UI.Tests.fullName.salutations.indexOf(y.toLowerCase()) == -1;
80
+ });
81
+ });
82
+
83
+ var score = 0, i, j, k = p[0].length, l = p[1].length, x, y;
84
+
85
+ for(i = 0; i < k; i++) {
86
+ for(j = 0; j < l; j++) {
87
+
88
+ x = p[0][i]; // word from a
89
+ y = p[1][j]; // word from b
90
+
91
+ // whole word match (with case):
92
+ if(x == y) {
93
+ score += 1;
94
+ }
95
+
96
+ // go case insensitive
97
+ else {
98
+ x = x.toLowerCase();
99
+ y = y.toLowerCase();
100
+
101
+ // whole word match:
102
+ if(x == y) {
103
+ score += .8;
104
+ }
105
+
106
+ // shortened version (eg Rob for Robert or J for John):
107
+ else if(x.indexOf(y) == 0 || y.indexOf(x) == 0) {
108
+ score += .5;
109
+ }
110
+
111
+ // contained (eg Liz for Elizabeth):
112
+ else if(x.indexOf(y) >= 0 || y.indexOf(x) >= 0) {
113
+ score += .3;
114
+ }
115
+
116
+ // first letter (eg Jim for James):
117
+ else if(x.substr(0, 1) == y.substr(0, 1)) {
118
+ score += .1;
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // max score will be 0.8. normalise to highest word count.
125
+ return .8 * score / Math.max(p[0].length, p[1].length);
126
+ },
127
+
128
+ string: function(a, b) {
129
+
130
+ if(a == b) {
131
+ return 1;
132
+ }
133
+
134
+ a = a || '';
135
+ b = b || '';
136
+
137
+ var i, x, y;
138
+ if(a.length > b.length) {
139
+ x = a.toLowerCase().trim();
140
+ y = b.toLowerCase().trim();
141
+ } else {
142
+ x = b.toLowerCase().trim();
143
+ y = a.toLowerCase().trim();
144
+ }
145
+
146
+ if(x === '' || y === '') {
147
+ return 0;
148
+ }
149
+
150
+ if(x.indexOf(y) != -1) {
151
+ return .95 * y.length / x.length;
152
+ }
153
+
154
+ return 0;
155
+
156
+ },
157
+
158
+ amount: function(a, b) {
159
+
160
+ if(a == b) {
161
+ return 1;
162
+ }
163
+
164
+ var x = parseFloat((a || 0).toString().replace(/[^-.\d]+/g, '')),
165
+ y = parseFloat((b || 0).toString().replace(/[^-.\d]+/g, ''));
166
+
167
+ if(isNaN(x) || isNaN(y)) {
168
+ return 0;
169
+ }
170
+
171
+ if(x == y) {
172
+ return 1;
173
+ }
174
+
175
+ if(x == 0 || y == 0) {
176
+ return 0;
177
+ }
178
+
179
+ return (x > y ? y / x : x / y) * 1.8 - 1;
180
+
181
+ }
182
+ }
183
+
184
+ };
185
+
186
+ nf.UI.Pager.prototype = {
187
+ goTo: function(dest) {
188
+ this.current = Math.max(Math.min(this.total, dest), 1);
189
+ this.update();
190
+ this.owner.update();
191
+ },
192
+
193
+ update: function(totalItems, itemsPerPage) {
194
+ if(typeof totalItems == 'number' && typeof itemsPerPage == 'number') {
195
+ this.total = Math.max(1, Math.ceil(totalItems / itemsPerPage));
196
+ this.current = Math.max(Math.min(this.current, this.total), 1);
197
+ }
198
+ this.currentElement.innerHTML = this.current.toString();
199
+ this.totalElement.innerHTML = this.total.toString();
200
+ nf.className(this.rootElement, 'bof', this.current == 1 ? 1 : -1);
201
+ nf.className(this.rootElement, 'eof', this.current == this.total ? 1 : -1);
202
+ },
203
+
204
+ init: function() {
205
+
206
+ var l = nf.UI.Labels, pager = this;
207
+
208
+ nf('<a>', this.rootElement, {
209
+ className: 'first',
210
+ href: 'javascript:',
211
+ innerHTML: l.goToFirst.toHTML(),
212
+ onclick: function() {
213
+ pager.goTo(1)
214
+ }
215
+ });
216
+
217
+ nf('<', this.rootElement, ' ');
218
+
219
+ nf('<a>', this.rootElement, {
220
+ className: 'previous',
221
+ href: 'javascript:',
222
+ innerHTML: l.goToPrevious.toHTML(),
223
+ onclick: function() {
224
+ pager.goTo(pager.current - 1)
225
+ }
226
+ });
227
+
228
+ nf('<', this.rootElement, ' ');
229
+
230
+ this.currentElement = nf('<span>', this.rootElement, {className: 'current'});
231
+
232
+ nf('<', this.rootElement, ' ');
233
+
234
+ nf('<span>', this.rootElement, {className: 'of', innerHTML: l.of.toHTML()});
235
+
236
+ nf('<', this.rootElement, ' ');
237
+
238
+ this.totalElement = nf('<span>', this.rootElement, {className: 'total'});
239
+
240
+ nf('<', this.rootElement, ' ');
241
+
242
+ nf('<a>', this.rootElement, {
243
+ className: 'next',
244
+ href: 'javascript:',
245
+ innerHTML: l.goToNext.toHTML(),
246
+ onclick: function() {
247
+ pager.goTo(pager.current + 1)
248
+ }
249
+ });
250
+
251
+ nf('<', this.rootElement, ' ');
252
+
253
+ nf('<a>', this.rootElement, {
254
+ className: 'last',
255
+ href: 'javascript:',
256
+ innerHTML: l.goToLast.toHTML(),
257
+ onclick: function() {
258
+ pager.goTo(pager.total)
259
+ }
260
+ });
261
+
262
+ this.update();
263
+ }
264
+ };
265
+
266
+ nf.UI.Actions.prototype = {
267
+ add: function(id, label, callback) {
268
+ if(!(id in this.items)) {
269
+ this.items[id] = [label, callback];
270
+ this.redraw();
271
+ }
272
+ },
273
+ remove: function(id) {
274
+ if(id in this.items) {
275
+ delete this.items[id];
276
+ this.redraw();
277
+ }
278
+ },
279
+ redraw: function() {
280
+ if(!this.rootElement) {
281
+ return;
282
+ }
283
+ this.rootElement.innerHTML = '';
284
+ var i = null;
285
+ for(i in this.items) {
286
+ nf('<a>', nf('<li>', this.rootElement, {className: i}), {
287
+ href: 'javascript:',
288
+ innerHTML: this.items[i][0].toHTML(),
289
+ onclick: (function(c, that) {
290
+ return function() {
291
+ c.call(that)
292
+ }
293
+ })(this.items[i][1], this.owner)
294
+ });
295
+ }
296
+ if(i) {
297
+ nf.className(this.rootElement.firstChild, 'first', 1);
298
+ nf.className(this.rootElement.childNodes[this.rootElement.childNodes.length - 1], 'last', 1);
299
+ }
300
+ this.redrawSearch();
301
+ },
302
+ redrawSearch: function() {
303
+
304
+ var searchValue = this.searchField ? this.searchField.value : '',
305
+ id, li, l, o;
306
+
307
+ if(this.owner.searchable) {
308
+
309
+ l = nf.UI.Labels.search;
310
+
311
+ li = nf('<li>', this.rootElement, {className: 'last search'});
312
+ if(this.rootElement.childNodes.length == 1) {
313
+ nf.className(li, 'first', 1);
314
+ }
315
+ else {
316
+ nf.className(li.previousSibling, 'last', -1);
317
+ }
318
+
319
+ id = this.owner.appId + 'Search';
320
+
321
+ nf('<label>', li, {
322
+ innerHTML: l.toHTML(),
323
+ htmlFor: id
324
+ });
325
+
326
+ o = this.owner;
327
+
328
+ this.searchField = nf('<input>', li, {
329
+ placeholder: l,
330
+ title: l,
331
+ type: 'text',
332
+ id: id,
333
+ value: searchValue,
334
+ onkeyup: function() {
335
+ o.update()
336
+ }
337
+ });
338
+
339
+ }
340
+ }
341
+ };
342
+
343
+ // list view class
344
+ nf.ListView = function(rootElement) {
345
+ this.rootElement = rootElement;
346
+ this.clear();
347
+ };
348
+
349
+ // list view class
350
+ nf.ListView.prototype = {
351
+ addItem: function() {
352
+ var ret = nf('<li>');
353
+ this.rootElement.appendChild(ret);
354
+ this.refreshClasses();
355
+ return ret;
356
+ },
357
+ activateItem: function(item) {
358
+ if(typeof item == 'number') {
359
+ item = this.rootElement.childNodes[item];
360
+ }
361
+ var i = this.rootElement.childNodes.length;
362
+ while(i--) {
363
+ nf.className(
364
+ this.rootElement.childNodes[i],
365
+ 'active',
366
+ this.rootElement.childNodes[i] === item ? 1 : -1
367
+ );
368
+ }
369
+ },
370
+ removeItem: function(item) {
371
+ if(typeof item == 'number') {
372
+ item = this.rootElement.childNodes[item];
373
+ }
374
+ this.rootElement.removeChild(item);
375
+ this.refreshClasses();
376
+ },
377
+ refreshClasses: function() {
378
+ var c = this.rootElement.childNodes,
379
+ i = c.length;
380
+ while(i--) {
381
+ nf.className(c[i], 'first', !i ? 1 : -1);
382
+ nf.className(c[i], 'last', i == c.length - 1 ? 1 : -1);
383
+ }
384
+ },
385
+ clear: function() {
386
+ if(this.rootElement) {
387
+ this.rootElement.innerHTML = '';
388
+ }
389
+ }
390
+ };
391
+
392
+ nf.UI.Tests.fullName.salutations = "mr mrs ms dr prof".split(' ');
393
+
394
+ nf.UI.Tests.email.variation = /\.(com|net|org|gov|edu|[a-z]{1,2})\b.*|\.+$/i;