fustrate-rails 0.3.3

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/lib/fustrate-rails.rb +4 -0
  3. data/lib/fustrate/rails/engine.rb +14 -0
  4. data/lib/fustrate/rails/version.rb +6 -0
  5. data/vendor/assets/javascripts/awesomplete.js +402 -0
  6. data/vendor/assets/javascripts/fustrate.coffee +6 -0
  7. data/vendor/assets/javascripts/fustrate/_module.coffee +134 -0
  8. data/vendor/assets/javascripts/fustrate/components/_module.coffee +3 -0
  9. data/vendor/assets/javascripts/fustrate/components/alert_box.coffee +10 -0
  10. data/vendor/assets/javascripts/fustrate/components/autocomplete.coffee +161 -0
  11. data/vendor/assets/javascripts/fustrate/components/disclosure.coffee +12 -0
  12. data/vendor/assets/javascripts/fustrate/components/drop_zone.coffee +9 -0
  13. data/vendor/assets/javascripts/fustrate/components/dropdown.coffee +48 -0
  14. data/vendor/assets/javascripts/fustrate/components/file_picker.coffee +10 -0
  15. data/vendor/assets/javascripts/fustrate/components/flash.coffee +31 -0
  16. data/vendor/assets/javascripts/fustrate/components/modal.coffee +213 -0
  17. data/vendor/assets/javascripts/fustrate/components/pagination.coffee +84 -0
  18. data/vendor/assets/javascripts/fustrate/components/tabs.coffee +28 -0
  19. data/vendor/assets/javascripts/fustrate/components/tooltip.coffee +72 -0
  20. data/vendor/assets/javascripts/fustrate/generic_form.coffee +31 -0
  21. data/vendor/assets/javascripts/fustrate/generic_page.coffee +40 -0
  22. data/vendor/assets/javascripts/fustrate/generic_table.coffee +57 -0
  23. data/vendor/assets/javascripts/fustrate/listenable.coffee +25 -0
  24. data/vendor/assets/javascripts/fustrate/object.coffee +21 -0
  25. data/vendor/assets/javascripts/fustrate/record.coffee +23 -0
  26. data/vendor/assets/stylesheets/_fustrate.sass +7 -0
  27. data/vendor/assets/stylesheets/awesomplete.sass +75 -0
  28. data/vendor/assets/stylesheets/fustrate/_colors.sass +9 -0
  29. data/vendor/assets/stylesheets/fustrate/_settings.sass +20 -0
  30. data/vendor/assets/stylesheets/fustrate/components/_components.sass +36 -0
  31. data/vendor/assets/stylesheets/fustrate/components/_functions.sass +40 -0
  32. data/vendor/assets/stylesheets/fustrate/components/alerts.sass +78 -0
  33. data/vendor/assets/stylesheets/fustrate/components/buttons.sass +103 -0
  34. data/vendor/assets/stylesheets/fustrate/components/disclosures.sass +23 -0
  35. data/vendor/assets/stylesheets/fustrate/components/dropdowns.sass +31 -0
  36. data/vendor/assets/stylesheets/fustrate/components/flash.sass +33 -0
  37. data/vendor/assets/stylesheets/fustrate/components/forms.sass +188 -0
  38. data/vendor/assets/stylesheets/fustrate/components/grid.sass +204 -0
  39. data/vendor/assets/stylesheets/fustrate/components/labels.sass +63 -0
  40. data/vendor/assets/stylesheets/fustrate/components/modals.sass +119 -0
  41. data/vendor/assets/stylesheets/fustrate/components/pagination.sass +57 -0
  42. data/vendor/assets/stylesheets/fustrate/components/panels.sass +49 -0
  43. data/vendor/assets/stylesheets/fustrate/components/popovers.sass +15 -0
  44. data/vendor/assets/stylesheets/fustrate/components/tables.sass +58 -0
  45. data/vendor/assets/stylesheets/fustrate/components/tabs.sass +44 -0
  46. data/vendor/assets/stylesheets/fustrate/components/tooltips.sass +28 -0
  47. data/vendor/assets/stylesheets/fustrate/components/typography.sass +355 -0
  48. metadata +211 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d5f71aa31c1e5bad989dc3c933c549a5e62f1a5
4
+ data.tar.gz: 9a42f14c3770a006361c62eb23fa4403d0679024
5
+ SHA512:
6
+ metadata.gz: 91bf081fd1025eeea96885535db9b86837cb05d9f6f702d9c1c16b49f33c6a6765f25ae9188eb48af9f34d60b4451461d0da6a97c0fc2d264c5054360a3c5380
7
+ data.tar.gz: 97193c0ca4c61d7b5fed80b4f98d6eea38a961a8799ebafa4d04a9cb5910de93dc96f5822c4b391f93474d4068b656363b2c64f59d6283dd3b9a9879c6153254
@@ -0,0 +1,4 @@
1
+ require 'coffee-script'
2
+ require 'sass'
3
+ require 'fustrate/rails/engine'
4
+ require 'fustrate/rails/version'
@@ -0,0 +1,14 @@
1
+ require 'rails/engine'
2
+
3
+ require 'bourbon'
4
+ require 'js-routes'
5
+ require 'modernizr-rails'
6
+ require 'momentjs-rails'
7
+ require 'font-awesome-rails'
8
+
9
+ module Fustrate
10
+ module Rails
11
+ class Engine < ::Rails::Engine
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module Fustrate
3
+ module Rails
4
+ VERSION = '0.3.3'.freeze
5
+ end
6
+ end
@@ -0,0 +1,402 @@
1
+ /* 1795543d988d0fd9ca6237a5ac176f8e88d63990 */
2
+
3
+ /**
4
+ * Simple, lightweight, usable local autocomplete library for modern browsers
5
+ * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P
6
+ * @author Lea Verou http://leaverou.github.io/awesomplete
7
+ * MIT license
8
+ */
9
+
10
+ (function () {
11
+
12
+ var _ = function (input, o) {
13
+ var me = this;
14
+
15
+ // Setup
16
+
17
+ this.input = $(input);
18
+ this.input.setAttribute("autocomplete", "off");
19
+ this.input.setAttribute("aria-autocomplete", "list");
20
+
21
+ o = o || {};
22
+
23
+ configure(this, {
24
+ minChars: 2,
25
+ maxItems: 10,
26
+ autoFirst: false,
27
+ filter: _.FILTER_CONTAINS,
28
+ sort: _.SORT_BYLENGTH,
29
+ item: _.ITEM,
30
+ replace: _.REPLACE
31
+ }, o);
32
+
33
+ this.index = -1;
34
+
35
+ // Create necessary elements
36
+
37
+ this.container = $.create("div", {
38
+ className: "awesomplete",
39
+ around: input
40
+ });
41
+
42
+ this.ul = $.create("ul", {
43
+ hidden: "hidden",
44
+ inside: this.container
45
+ });
46
+
47
+ this.status = $.create("span", {
48
+ className: "visually-hidden",
49
+ role: "status",
50
+ "aria-live": "assertive",
51
+ "aria-relevant": "additions",
52
+ inside: this.container
53
+ });
54
+
55
+ // Bind events
56
+
57
+ $.bind(this.input, {
58
+ "input": this.evaluate.bind(this),
59
+ "blur": this.close.bind(this),
60
+ "keydown": function(evt) {
61
+ var c = evt.keyCode;
62
+
63
+ // If the dropdown `ul` is in view, then act on keydown for the following keys:
64
+ // Enter / Esc / Up / Down
65
+ if(me.opened) {
66
+ if (c === 13 && me.selected) { // Enter
67
+ evt.preventDefault();
68
+ me.select();
69
+ }
70
+ else if (c === 27) { // Esc
71
+ me.close();
72
+ }
73
+ else if (c === 38 || c === 40) { // Down/Up arrow
74
+ evt.preventDefault();
75
+ me[c === 38? "previous" : "next"]();
76
+ }
77
+ }
78
+ }
79
+ });
80
+
81
+ $.bind(this.input.form, {"submit": this.close.bind(this)});
82
+
83
+ $.bind(this.ul, {"mousedown": function(evt) {
84
+ var li = evt.target;
85
+
86
+ if (li !== this) {
87
+
88
+ while (li && !/li/i.test(li.nodeName)) {
89
+ li = li.parentNode;
90
+ }
91
+
92
+ if (li && evt.button === 0) { // Only select on left click
93
+ evt.preventDefault();
94
+ me.select(li, evt.target);
95
+ }
96
+ }
97
+ }});
98
+
99
+ if (this.input.hasAttribute("list")) {
100
+ this.list = "#" + this.input.getAttribute("list");
101
+ this.input.removeAttribute("list");
102
+ }
103
+ else {
104
+ this.list = this.input.getAttribute("data-list") || o.list || [];
105
+ }
106
+
107
+ _.all.push(this);
108
+ };
109
+
110
+ _.prototype = {
111
+ set list(list) {
112
+ if (Array.isArray(list)) {
113
+ this._list = list;
114
+ }
115
+ else if (typeof list === "string" && list.indexOf(",") > -1) {
116
+ this._list = list.split(/\s*,\s*/);
117
+ }
118
+ else { // Element or CSS selector
119
+ list = $(list);
120
+
121
+ if (list && list.children) {
122
+ this._list = slice.apply(list.children).map(function (el) {
123
+ return el.textContent.trim();
124
+ });
125
+ }
126
+ }
127
+
128
+ if (document.activeElement === this.input) {
129
+ this.evaluate();
130
+ }
131
+ },
132
+
133
+ get selected() {
134
+ return this.index > -1;
135
+ },
136
+
137
+ get opened() {
138
+ return !this.ul.hasAttribute("hidden");
139
+ },
140
+
141
+ close: function () {
142
+ this.ul.setAttribute("hidden", "");
143
+ this.index = -1;
144
+
145
+ $.fire(this.input, "awesomplete-close");
146
+ },
147
+
148
+ open: function () {
149
+ this.ul.removeAttribute("hidden");
150
+
151
+ if (this.autoFirst && this.index === -1) {
152
+ this.goto(0);
153
+ }
154
+
155
+ $.fire(this.input, "awesomplete-open");
156
+ },
157
+
158
+ next: function () {
159
+ var count = this.ul.children.length;
160
+
161
+ this.goto(this.index < count - 1? this.index + 1 : -1);
162
+ },
163
+
164
+ previous: function () {
165
+ var count = this.ul.children.length;
166
+
167
+ this.goto(this.selected? this.index - 1 : count - 1);
168
+ },
169
+
170
+ // Should not be used, highlights specific item without any checks!
171
+ goto: function (i) {
172
+ var lis = this.ul.children;
173
+
174
+ if (this.selected) {
175
+ lis[this.index].setAttribute("aria-selected", "false");
176
+ }
177
+
178
+ this.index = i;
179
+
180
+ if (i > -1 && lis.length > 0) {
181
+ lis[i].setAttribute("aria-selected", "true");
182
+ this.status.textContent = lis[i].textContent;
183
+ }
184
+
185
+ $.fire(this.input, "awesomplete-highlight");
186
+ },
187
+
188
+ select: function (selected, origin) {
189
+ selected = selected || this.ul.children[this.index];
190
+
191
+ if (selected) {
192
+ var allowed = $.fire(this.input, "awesomplete-select", {
193
+ text: selected.textContent,
194
+ data: this.suggestions[$.siblingIndex(selected)],
195
+ origin: origin || selected
196
+ });
197
+
198
+ if (allowed) {
199
+ this.replace(selected.textContent);
200
+ this.close();
201
+ $.fire(this.input, "awesomplete-selectcomplete");
202
+ }
203
+ }
204
+ },
205
+
206
+ evaluate: function() {
207
+ var me = this;
208
+ var value = this.input.value;
209
+
210
+ if (value.length >= this.minChars && this._list.length > 0) {
211
+ this.index = -1;
212
+ // Populate list with options that match
213
+ this.ul.innerHTML = "";
214
+
215
+ this.suggestions = this._list
216
+ .filter(function(item) {
217
+ return me.filter(item, value);
218
+ })
219
+ .sort(this.sort)
220
+ .slice(0, this.maxItems);
221
+
222
+ this.suggestions.forEach(function(text) {
223
+ me.ul.appendChild(me.item(text, value));
224
+ });
225
+
226
+ if (this.ul.children.length === 0) {
227
+ this.close();
228
+ } else {
229
+ this.open();
230
+ }
231
+ }
232
+ else {
233
+ this.close();
234
+ }
235
+ }
236
+ };
237
+
238
+ // Static methods/properties
239
+
240
+ _.all = [];
241
+
242
+ _.FILTER_CONTAINS = function (text, input) {
243
+ return RegExp($.regExpEscape(input.trim()), "i").test(text);
244
+ };
245
+
246
+ _.FILTER_STARTSWITH = function (text, input) {
247
+ return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text);
248
+ };
249
+
250
+ _.SORT_BYLENGTH = function (a, b) {
251
+ if (a.length !== b.length) {
252
+ return a.length - b.length;
253
+ }
254
+
255
+ return a < b? -1 : 1;
256
+ };
257
+
258
+ _.ITEM = function (text, input) {
259
+ var html = input === '' ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "<mark>$&</mark>");
260
+ return $.create("li", {
261
+ innerHTML: html,
262
+ "aria-selected": "false"
263
+ });
264
+ };
265
+
266
+ _.REPLACE = function (text) {
267
+ this.input.value = text;
268
+ };
269
+
270
+ // Private functions
271
+
272
+ function configure(instance, properties, o) {
273
+ for (var i in properties) {
274
+ var initial = properties[i],
275
+ attrValue = instance.input.getAttribute("data-" + i.toLowerCase());
276
+
277
+ if (typeof initial === "number") {
278
+ instance[i] = parseInt(attrValue);
279
+ }
280
+ else if (initial === false) { // Boolean options must be false by default anyway
281
+ instance[i] = attrValue !== null;
282
+ }
283
+ else if (initial instanceof Function) {
284
+ instance[i] = null;
285
+ }
286
+ else {
287
+ instance[i] = attrValue;
288
+ }
289
+
290
+ if (!instance[i] && instance[i] !== 0) {
291
+ instance[i] = (i in o)? o[i] : initial;
292
+ }
293
+ }
294
+ }
295
+
296
+ // Helpers
297
+
298
+ var slice = Array.prototype.slice;
299
+
300
+ function $(expr, con) {
301
+ return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
302
+ }
303
+
304
+ function $$(expr, con) {
305
+ return slice.call((con || document).querySelectorAll(expr));
306
+ }
307
+
308
+ $.create = function(tag, o) {
309
+ var element = document.createElement(tag);
310
+
311
+ for (var i in o) {
312
+ var val = o[i];
313
+
314
+ if (i === "inside") {
315
+ $(val).appendChild(element);
316
+ }
317
+ else if (i === "around") {
318
+ var ref = $(val);
319
+ ref.parentNode.insertBefore(element, ref);
320
+ element.appendChild(ref);
321
+ }
322
+ else if (i in element) {
323
+ element[i] = val;
324
+ }
325
+ else {
326
+ element.setAttribute(i, val);
327
+ }
328
+ }
329
+
330
+ return element;
331
+ };
332
+
333
+ $.bind = function(element, o) {
334
+ if (element) {
335
+ for (var event in o) {
336
+ var callback = o[event];
337
+
338
+ event.split(/\s+/).forEach(function (event) {
339
+ element.addEventListener(event, callback);
340
+ });
341
+ }
342
+ }
343
+ };
344
+
345
+ $.fire = function(target, type, properties) {
346
+ var evt = document.createEvent("HTMLEvents");
347
+
348
+ evt.initEvent(type, true, true );
349
+
350
+ for (var j in properties) {
351
+ evt[j] = properties[j];
352
+ }
353
+
354
+ return target.dispatchEvent(evt);
355
+ };
356
+
357
+ $.regExpEscape = function (s) {
358
+ return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
359
+ };
360
+
361
+ $.siblingIndex = function (el) {
362
+ /* eslint-disable no-cond-assign */
363
+ for (var i = 0; el = el.previousElementSibling; i++);
364
+ return i;
365
+ };
366
+
367
+ // Initialization
368
+
369
+ function init() {
370
+ $$("input.awesomplete").forEach(function (input) {
371
+ new _(input);
372
+ });
373
+ }
374
+
375
+ // Are we in a browser? Check for Document constructor
376
+ if (typeof Document !== "undefined") {
377
+ // DOM already loaded?
378
+ if (document.readyState !== "loading") {
379
+ init();
380
+ }
381
+ else {
382
+ // Wait for it
383
+ document.addEventListener("DOMContentLoaded", init);
384
+ }
385
+ }
386
+
387
+ _.$ = $;
388
+ _.$$ = $$;
389
+
390
+ // Make sure to export Awesomplete on self when in a browser
391
+ if (typeof self !== "undefined") {
392
+ self.Awesomplete = _;
393
+ }
394
+
395
+ // Expose Awesomplete as a CJS module
396
+ if (typeof module === "object" && module.exports) {
397
+ module.exports = _;
398
+ }
399
+
400
+ return _;
401
+
402
+ }());
@@ -0,0 +1,6 @@
1
+ #= require js-routes
2
+ #= require moment
3
+ #= require awesomplete
4
+ #
5
+ #= require_directory ./fustrate
6
+ #= require_tree ./fustrate
@@ -0,0 +1,134 @@
1
+ #= require_self
2
+ #= require_directory .
3
+ #= require_tree .
4
+
5
+ class window.Fustrate
6
+ @VERSION: '0.3.3'
7
+ @libs: {}
8
+
9
+ constructor: ->
10
+ lib.init() for own name, lib of @constructor.libs
11
+
12
+ for own component of Fustrate.Components
13
+ Fustrate.Components[component].initialize()
14
+
15
+ @initialize()
16
+
17
+ initialize: ->
18
+ $('[data-js-class]').each (index, elem) ->
19
+ element = $(elem)
20
+ klass = Fustrate._stringToClass element.data('js-class')
21
+
22
+ element.data 'js-object', new klass(element)
23
+
24
+ $.ajaxSetup
25
+ cache: false
26
+ beforeSend: $.rails.CSRFProtection
27
+
28
+ $('table').wrap '<div class="responsive-table"></div>'
29
+
30
+ $('.number').each (index, elem) ->
31
+ elem = $ @
32
+
33
+ number = if elem.data('number') != undefined
34
+ elem.data('number')
35
+ else
36
+ elem.html()
37
+
38
+ elem.addClass 'negative' if parseInt(number, 10) < 0
39
+
40
+ @_stringToClass: (string) ->
41
+ pieces = string.split('.')
42
+
43
+ Fustrate._arrayToClass(pieces, window)
44
+
45
+ @_arrayToClass: (pieces, root) ->
46
+ if pieces.length == 1
47
+ root[pieces[0]]
48
+ else
49
+ Fustrate._arrayToClass pieces.slice(1), root[pieces[0]]
50
+
51
+ @linkTo: (text, path, options = {}) ->
52
+ $('<a>').prop('href', path).html(text).prop(options).outerHTML()
53
+
54
+ @ajaxUpload: (url, data) ->
55
+ formData = new FormData
56
+
57
+ formData.append key, value for key, value of data
58
+
59
+ $.ajax
60
+ url: url
61
+ type: 'POST'
62
+ data: formData
63
+ processData: false
64
+ contentType: false
65
+ dataType: 'script'
66
+ beforeSend: (xhr) ->
67
+ $.rails.CSRFProtection xhr
68
+
69
+ @humanDate: (date, time = false) ->
70
+ if date.year() == moment().year()
71
+ date.format("M/D#{if time then ' h:mm A' else ''}")
72
+ else
73
+ date.format("M/D/YY#{if time then ' h:mm A' else ''}")
74
+
75
+ @label: (text, type) ->
76
+ type = if type then "#{type} " else ""
77
+
78
+ $('<span>')
79
+ .text(text)
80
+ .prop('class', "label #{type}#{text}".toLowerCase())
81
+
82
+ @icon: (types) ->
83
+ classes = ("fa-#{type}" for type in types.split(' ')).join(' ')
84
+
85
+ "<i class=\"fa #{classes}\"></i>"
86
+
87
+ String::titleize = ->
88
+ @replace(/_/g, ' ').replace /\b[a-z]/g, (char) -> char.toUpperCase()
89
+
90
+ String::capitalize = ->
91
+ @charAt(0).toUpperCase() + @slice(1)
92
+
93
+ String::phoneFormat = ->
94
+ if /^(\d+)(ext|x)(\d+)$/.test @
95
+ @replace /(\d+)(ext|x)(\d+)/, ->
96
+ arguments[1].phoneFormat() + ' x' + arguments[3]
97
+ else if /^1\d{10}$/.test @
98
+ @replace /1(\d{3})(\d{3})(\d{4})/, '1 ($1) $2-$3'
99
+ else if /^\d{10}$/.test @
100
+ @replace /(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'
101
+ else if /^\d{7}$/.test @
102
+ @replace /(\d{3})(\d{4})/, '$1-$2'
103
+ else
104
+ @
105
+
106
+ Array::toSentence = ->
107
+ switch @length
108
+ when 0 then ''
109
+ when 1 then @[0]
110
+ when 2 then "#{@[0]} and #{@[1]}"
111
+ else "#{@slice(0, -1).join(', ')}, and #{@[@length - 1]}"
112
+
113
+ Array::remove = (object) ->
114
+ index = @indexOf object
115
+ @splice index, 1 if index isnt -1
116
+
117
+ Array::first = ->
118
+ @[0]
119
+
120
+ Array::last = ->
121
+ @[@length - 1]
122
+
123
+ Array::peek = Array::last
124
+
125
+ # Used to define getters and setters
126
+ Function::define = (name, methods) ->
127
+ Object.defineProperty @::, name, methods
128
+
129
+ jQuery.fn.outerHTML = ->
130
+ return '' unless @length
131
+
132
+ return @[0].outerHTML if @[0].outerHTML
133
+
134
+ $('<div>').append(@[0].clone()).remove().html()