blissful-rails 0.1.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.
@@ -0,0 +1,625 @@
1
+ (function() {
2
+ "use strict";
3
+
4
+ function overload(callback, start, end) {
5
+ start = start === undefined ? 1 : start;
6
+ end = end || start + 1;
7
+
8
+ if (end - start <= 1) {
9
+ return function() {
10
+ if ($.type(arguments[start]) === 'string') {
11
+ return callback.apply(this, arguments);
12
+ }
13
+
14
+ var obj = arguments[start], ret;
15
+
16
+ for (var key in obj) {
17
+ var args = Array.from(arguments);
18
+ args.splice(start, 1, key, obj[key]);
19
+ ret = callback.apply(this, args);
20
+ }
21
+
22
+ return ret;
23
+ };
24
+ }
25
+
26
+ return overload(overload(callback, start + 1, end), start, end - 1);
27
+ }
28
+
29
+ // Copy properties from one object to another. Overwrites allowed.
30
+ function extend(to, from, whitelist) {
31
+ for (var property in from) {
32
+ if (whitelist) {
33
+ var type = $.type(whitelist);
34
+
35
+ if (whitelist === "own" && !from.hasOwnProperty(property) ||
36
+ type === "array" && whitelist.indexOf(property) === -1 ||
37
+ type === "regexp" && !whitelist.test(property) ||
38
+ type === "function" && !whitelist.call(from, property)) {
39
+ continue;
40
+ }
41
+ }
42
+
43
+ // To copy gettters/setters, preserve flags etc
44
+ var descriptor = Object.getOwnPropertyDescriptor(from, property);
45
+
46
+ if (descriptor && (!descriptor.writable || !descriptor.configurable || !descriptor.enumerable || descriptor.get || descriptor.set)) {
47
+ delete to[property];
48
+ Object.defineProperty(to, property, descriptor);
49
+ }
50
+ else {
51
+ to[property] = from[property];
52
+ }
53
+ }
54
+
55
+ return to;
56
+ }
57
+
58
+ var $ = self.Bliss = extend(function(expr, context) {
59
+ return $.type(expr) === "string"? (context || document).querySelector(expr) : expr || null;
60
+ }, self.Bliss);
61
+
62
+ extend($, {
63
+ extend: extend,
64
+
65
+ overload: overload,
66
+
67
+ property: $.property || "_",
68
+
69
+ sources: {},
70
+
71
+ noop: function(){},
72
+
73
+ $: function(expr, context) {
74
+ if (expr instanceof Node || expr instanceof Window) {
75
+ return [expr];
76
+ }
77
+
78
+ return Array.from(typeof expr == "string"? (context || document).querySelectorAll(expr) : expr || []);
79
+ },
80
+
81
+ /**
82
+ * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc)
83
+ */
84
+ type: function(obj) {
85
+ if (obj === null) { return 'null'; }
86
+
87
+ if (obj === undefined) { return 'undefined'; }
88
+
89
+ var ret = (Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
90
+
91
+ if(ret == 'number' && isNaN(obj)) {
92
+ return 'nan';
93
+ }
94
+
95
+ return ret;
96
+ },
97
+
98
+ /*
99
+ * Return first non-undefined value. Mainly used internally.
100
+ */
101
+ defined: function () {
102
+ for (var i=0; i<arguments.length; i++) {
103
+ if (arguments[i] !== undefined) {
104
+ return arguments[i];
105
+ }
106
+ }
107
+ },
108
+
109
+ create: function (tag, o) {
110
+ // 4 signatures: (tag, o), (tag), (o), ()
111
+ if (arguments.length === 1) {
112
+ if ($.type(tag) === "string") {
113
+ o = {};
114
+ }
115
+ else {
116
+ o = tag;
117
+ tag = o.tag;
118
+ delete o.tag;
119
+ }
120
+ }
121
+
122
+ return $.set(document.createElement(tag || "div"), o);
123
+ },
124
+
125
+ each: function(obj, callback, ret) {
126
+ ret = ret || {};
127
+
128
+ for (var property in obj) {
129
+ ret[property] = callback.call(obj, property, obj[property]);
130
+ }
131
+
132
+ return ret;
133
+ },
134
+
135
+ ready: function(context) {
136
+ context = context || document;
137
+
138
+ return new Promise(function(resolve, reject){
139
+ if (context.readyState !== "loading") {
140
+ resolve();
141
+ }
142
+ else {
143
+ context.addEventListener("DOMContentLoaded", function(){
144
+ resolve();
145
+ });
146
+ }
147
+ });
148
+ },
149
+
150
+ // Helper for defining OOP-like “classes”
151
+ Class: function(o) {
152
+ var init = $.noop;
153
+
154
+ if (o.hasOwnProperty("constructor")) {
155
+ init = o.constructor;
156
+ delete o.constructor;
157
+ }
158
+
159
+ var abstract = o.abstract;
160
+ delete o.abstract;
161
+
162
+ var ret = function() {
163
+ if (abstract && this.constructor === ret) {
164
+ throw new Error("Abstract classes cannot be directly instantiated.");
165
+ }
166
+
167
+ if (this.constructor.super && this.constructor.super != ret) {
168
+ // FIXME This should never happen, but for some reason it does if ret.super is null
169
+ // Debugging revealed that somehow this.constructor !== ret, wtf. Must look more into this
170
+ this.constructor.super.apply(this, arguments);
171
+ }
172
+
173
+ return init.apply(this, arguments);
174
+ };
175
+
176
+ ret.super = o.extends || null;
177
+ delete o.extends;
178
+
179
+ ret.prototype = $.extend(Object.create(ret.super? ret.super.prototype : Object), {
180
+ constructor: ret
181
+ });
182
+
183
+ $.extend(ret, o.static);
184
+ delete o.static;
185
+
186
+ for (var property in o) {
187
+ if (property in $.classProps) {
188
+ $.classProps[property].call(ret, ret.prototype, o[property]);
189
+ delete o[property];
190
+ }
191
+ }
192
+
193
+ // Anything that remains is an instance method/property or ret.prototype.constructor
194
+ $.extend(ret.prototype, o);
195
+
196
+ // For easier calling of super methods
197
+ // This doesn't save us from having to use .call(this) though
198
+ ret.prototype.super = ret.super? ret.super.prototype : null;
199
+
200
+ return ret;
201
+ },
202
+
203
+ // Properties with special handling in classes
204
+ classProps: {
205
+ // Lazily evaluated properties
206
+ lazy: overload(function(obj, property, getter) {
207
+ Object.defineProperty(obj, property, {
208
+ get: function() {
209
+ // FIXME this does not work for instances if property is defined on the prototype
210
+ delete this[property];
211
+ return this[property] = getter.call(this);
212
+ },
213
+ configurable: true,
214
+ enumerable: true
215
+ });
216
+ return obj;
217
+ }),
218
+
219
+ // Properties that behave like normal properties but also execute code upon getting/setting
220
+ live: overload(function(obj, property, descriptor) {
221
+ if ($.type(descriptor) === "function") {
222
+ descriptor = {set: descriptor};
223
+ }
224
+
225
+ Object.defineProperty(obj, property, {
226
+ get: function() {
227
+ var value = this["_" + property];
228
+ var ret = descriptor.get && descriptor.get.call(this, value);
229
+ return ret !== undefined? ret : value;
230
+ },
231
+ set: function(v) {
232
+ var value = this["_" + property];
233
+ var ret = descriptor.set && descriptor.set.call(this, v, value);
234
+ this["_" + property] = ret !== undefined? ret : v;
235
+ },
236
+ configurable: descriptor.configurable,
237
+ enumerable: descriptor.enumerable
238
+ });
239
+
240
+ return obj;
241
+ })
242
+
243
+ },
244
+
245
+ // Includes a script, returns a promise
246
+ include: function() {
247
+ var url = arguments[arguments.length - 1];
248
+ var loaded = arguments.length === 2? arguments[0] : false;
249
+
250
+ var script = document.createElement("script");
251
+
252
+ return loaded? Promise.resolve() : new Promise(function(resolve, reject){
253
+ $.set(script, {
254
+ async: true,
255
+ onload: function() {
256
+ resolve();
257
+ $.remove(script);
258
+ },
259
+ onerror: function() {
260
+ reject();
261
+ },
262
+ src: url,
263
+ inside: document.head
264
+ });
265
+ });
266
+
267
+ },
268
+
269
+ /*
270
+ * Fetch API inspired XHR wrapper. Returns promise.
271
+ */
272
+ fetch: function(url, o) {
273
+ if (!url) {
274
+ throw new TypeError("URL parameter is mandatory and cannot be " + url);
275
+ }
276
+
277
+ // Set defaults & fixup arguments
278
+ var env = extend({
279
+ url: new URL(url, location),
280
+ data: "",
281
+ method: "GET",
282
+ headers: {},
283
+ xhr: new XMLHttpRequest()
284
+ }, o);
285
+
286
+ env.method = env.method.toUpperCase();
287
+
288
+ $.hooks.run("fetch-args", env);
289
+
290
+ // Start sending the request
291
+
292
+ if (env.method === "GET" && env.data) {
293
+ env.url.search += env.data;
294
+ }
295
+
296
+ document.body.setAttribute('data-loading', env.url);
297
+
298
+ env.xhr.open(env.method, env.url.href, env.async !== false, env.user, env.password);
299
+
300
+ for (var property in o) {
301
+ if (property in env.xhr) {
302
+ try {
303
+ env.xhr[property] = o[property];
304
+ }
305
+ catch (e) {
306
+ self.console && console.error(e);
307
+ }
308
+ }
309
+ }
310
+
311
+ if (env.method !== 'GET' && !env.headers['Content-type'] && !env.headers['Content-Type']) {
312
+ env.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
313
+ }
314
+
315
+ for (var header in env.headers) {
316
+ env.xhr.setRequestHeader(header, env.headers[header]);
317
+ }
318
+
319
+ return new Promise(function(resolve, reject){
320
+ env.xhr.onload = function(){
321
+ document.body.removeAttribute('data-loading');
322
+
323
+ if (env.xhr.status === 0 || env.xhr.status >= 200 && env.xhr.status < 300 || env.xhr.status === 304) {
324
+ // Success!
325
+ resolve(env.xhr);
326
+ }
327
+ else {
328
+ reject(Error(env.xhr.statusText));
329
+ }
330
+
331
+ };
332
+
333
+ env.xhr.onerror = function() {
334
+ document.body.removeAttribute('data-loading');
335
+ reject(Error("Network Error"));
336
+ };
337
+
338
+ env.xhr.send(env.method === 'GET'? null : env.data);
339
+ });
340
+ },
341
+
342
+ value: function(obj) {
343
+ var hasRoot = $.type(obj) !== "string";
344
+
345
+ return $$(arguments).slice(+hasRoot).reduce(function(obj, property) {
346
+ return obj && obj[property];
347
+ }, hasRoot? obj : self);
348
+ }
349
+ });
350
+
351
+ $.Hooks = new $.Class({
352
+ add: function (name, callback) {
353
+ this[name] = this[name] || [];
354
+ this[name].push(callback);
355
+ },
356
+
357
+ run: function (name, env) {
358
+ (this[name] || []).forEach(function(callback) {
359
+ callback(env);
360
+ });
361
+ }
362
+ });
363
+
364
+ $.hooks = new $.Hooks();
365
+
366
+ var _ = $.property;
367
+
368
+ $.Element = function (subject) {
369
+ this.subject = subject;
370
+
371
+ // Author-defined element-related data
372
+ this.data = {};
373
+
374
+ // Internal Bliss element-related data
375
+ this.bliss = {};
376
+ };
377
+
378
+ $.Element.prototype = {
379
+ set: overload(function(property, value) {
380
+
381
+ if (property in $.setProps) {
382
+ $.setProps[property].call(this, value);
383
+ }
384
+ else if (property in this) {
385
+ this[property] = value;
386
+ }
387
+ else {
388
+ this.setAttribute(property, value);
389
+ }
390
+
391
+ }, 0),
392
+
393
+ // Run a CSS transition, return promise
394
+ transition: function(props, duration) {
395
+ duration = +duration || 400;
396
+
397
+ return new Promise(function(resolve, reject){
398
+ if ("transition" in this.style) {
399
+ // Get existing style
400
+ var previous = $.extend({}, this.style, /^transition(Duration|Property)$/);
401
+
402
+ $.style(this, {
403
+ transitionDuration: (duration || 400) + "ms",
404
+ transitionProperty: Object.keys(props).join(", ")
405
+ });
406
+
407
+ $.once(this, "transitionend", function(){
408
+ clearTimeout(i);
409
+ $.style(this, previous);
410
+ resolve(this);
411
+ });
412
+
413
+ // Failsafe, in case transitionend doesn’t fire
414
+ var i = setTimeout(resolve, duration+50, this);
415
+
416
+ $.style(this, props);
417
+ }
418
+ else {
419
+ $.style(this, props);
420
+ resolve(this);
421
+ }
422
+ }.bind(this));
423
+ },
424
+
425
+ // Fire a synthesized event on the element
426
+ fire: function (type, properties) {
427
+ var evt = document.createEvent("HTMLEvents");
428
+
429
+ evt.initEvent(type, true, true );
430
+
431
+ this.dispatchEvent($.extend(evt, properties));
432
+ }
433
+ };
434
+
435
+ /*
436
+ * Properties with custom handling in $.set()
437
+ * Also available as functions directly on element._ and on $
438
+ */
439
+ $.setProps = {
440
+ // Set a bunch of inline CSS styles
441
+ style: function (val) {
442
+ $.extend(this.style, val);
443
+ },
444
+
445
+ // Set a bunch of attributes
446
+ attributes: function (o) {
447
+ for (var attribute in o) {
448
+ this.setAttribute(attribute, o[attribute]);
449
+ }
450
+ },
451
+
452
+ // Set a bunch of properties on the element
453
+ properties: function (val) {
454
+ $.extend(this, val);
455
+ },
456
+
457
+ // Bind one or more events to the element
458
+ events: function (val) {
459
+ if (val && val.addEventListener) {
460
+ // Copy events from other element (requires Bliss Full)
461
+ var me = this;
462
+
463
+ // Copy listeners
464
+ if (val[_] && val[_].bliss) {
465
+ var listeners = val[_].bliss.listeners;
466
+
467
+ for (var type in listeners) {
468
+ listeners[type].forEach(function(l){
469
+ me.addEventListener(type, l.callback, l.capture);
470
+ });
471
+ }
472
+ }
473
+
474
+ // Copy inline events
475
+ for (var onevent in val) {
476
+ if (onevent.indexOf("on") === 0) {
477
+ this[onevent] = val[onevent];
478
+ }
479
+ }
480
+ }
481
+ else {
482
+ for (var events in val) {
483
+ events.split(/\s+/).forEach(function (event) {
484
+ this.addEventListener(event, val[events]);
485
+ }, this);
486
+ }
487
+ }
488
+ },
489
+
490
+ once: overload(function(events, callback){
491
+ events = events.split(/\s+/);
492
+ var me = this;
493
+ var once = function() {
494
+ events.forEach(function(event){
495
+ me.removeEventListener(event, once);
496
+ });
497
+
498
+ return callback.apply(me, arguments);
499
+ };
500
+
501
+ events.forEach(function (event) {
502
+ me.addEventListener(event, once);
503
+ });
504
+ }, 0),
505
+
506
+ // Event delegation
507
+ delegate: overload(function (type, selector, callback) {
508
+ this.addEventListener(type, function(evt) {
509
+ if (evt.target.closest(selector)) {
510
+ callback.call(this, evt);
511
+ }
512
+ });
513
+ }, 0, 2),
514
+
515
+ // Set the contents as a string, an element, an object to create an element or an array of these
516
+ contents: function (val) {
517
+ if (val || val === 0) {
518
+ (Array.isArray(val)? val : [val]).forEach(function (child) {
519
+ var type = $.type(child);
520
+
521
+ if (/^(string|number)$/.test(type)) {
522
+ child = document.createTextNode(child + "");
523
+ }
524
+ else if (type === "object") {
525
+ child = $.create(child);
526
+ }
527
+
528
+ if (child instanceof Node) {
529
+ this.appendChild(child);
530
+ }
531
+ }, this);
532
+ }
533
+ },
534
+
535
+ // Append the element inside another element
536
+ inside: function (element) {
537
+ element.appendChild(this);
538
+ },
539
+
540
+ // Insert the element before another element
541
+ before: function (element) {
542
+ element.parentNode.insertBefore(this, element);
543
+ },
544
+
545
+ // Insert the element after another element
546
+ after: function (element) {
547
+ element.parentNode.insertBefore(this, element.nextSibling);
548
+ },
549
+
550
+ // Insert the element before another element's contents
551
+ start: function (element) {
552
+ element.insertBefore(this, element.firstChild);
553
+ },
554
+
555
+ // Wrap the element around another element
556
+ around: function (element) {
557
+ if (element.parentNode) {
558
+ $.before(this, element);
559
+ }
560
+
561
+ (/^template$/i.test(this.nodeName)? this.content || this : this).appendChild(element);
562
+ }
563
+ };
564
+
565
+ $.Array = function (subject) {
566
+ this.subject = subject;
567
+ };
568
+
569
+ $.Array.prototype = {
570
+ all: function(method) {
571
+ var args = $$(arguments).slice(1);
572
+
573
+ return this[method].apply(this, args);
574
+ }
575
+ };
576
+
577
+ // Extends Bliss with more methods
578
+ $.add = overload(function(method, callback, on, noOverwrite) {
579
+ on = $.extend({$: true, element: true, array: true}, on);
580
+
581
+ if ($.type(callback) == "function") {
582
+ if (on.element && (!(method in $.Element.prototype) || !noOverwrite)) {
583
+ $.Element.prototype[method] = function () {
584
+ return this.subject && $.defined(callback.apply(this.subject, arguments), this.subject);
585
+ };
586
+ }
587
+
588
+ if (on.array && (!(method in $.Array.prototype) || !noOverwrite)) {
589
+ $.Array.prototype[method] = function() {
590
+ var args = arguments;
591
+ return this.subject.map(function(element) {
592
+ return element && $.defined(callback.apply(element, args), element);
593
+ });
594
+ };
595
+ }
596
+
597
+ if (on.$) {
598
+ $.sources[method] = $[method] = callback;
599
+
600
+ if (on.array || on.element) {
601
+ $[method] = function () {
602
+ var args = [].slice.apply(arguments);
603
+ var subject = args.shift();
604
+ var Type = on.array && Array.isArray(subject)? "Array" : "Element";
605
+
606
+ return $[Type].prototype[method].apply({subject: subject}, args);
607
+ };
608
+ }
609
+ }
610
+ }
611
+ }, 0);
612
+
613
+ $.add($.Array.prototype, {element: false});
614
+ $.add($.Element.prototype);
615
+ $.add($.setProps);
616
+ $.add($.classProps, {element: false, array: false});
617
+
618
+ // Add native methods on $ and _
619
+ var dummy = document.createElement("_");
620
+ $.add($.extend({}, HTMLElement.prototype, function(method){
621
+ return $.type(dummy[method]) === "function";
622
+ }), null, true);
623
+
624
+
625
+ })();