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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +80 -0
- data/Rakefile +10 -0
- data/blissful-rails.gemspec +21 -0
- data/lib/blissful.rb +4 -0
- data/lib/blissful/rails.rb +50 -0
- data/lib/blissful/rails/engine.rb +6 -0
- data/lib/blissful/rails/version.rb +5 -0
- data/vendor/assets/javascripts/bliss.js +722 -0
- data/vendor/assets/javascripts/bliss.shy.js +625 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: df8565fa42b627dc8e0d6d66ed7f87488cdbc7b4
|
4
|
+
data.tar.gz: 9336ccff34e99cf23227600a474c31ffe057f3d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d21fdbd67a22cd1e723f0d6c3bfe43adbde3d4ee82b33cf9b8ff0ee04c105214ff2382e945c0608d4ddfcaf97dad2a0a28accdd42f3734a5fa97a4bf9fb7d052
|
7
|
+
data.tar.gz: fa24cd7f09667f29f3d6b3c2012720cba17a803265ba378f389d5b2cfc543dc71a46a198c262bcfbc83e76f4822c564ef0178abcee0995cdf40b7737938525dd
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Spicy Magpie
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Bliss.js for Rails
|
2
|
+
|
3
|
+
This gem wraps the [Bliss.js](https://blissfuljs.com) library written by
|
4
|
+
[Lea Verou](http://lea.verou.me) and makes it available to the Rails Asset
|
5
|
+
Pipeline.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add the gem to the `Gemfile` in your Rails project:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'blissful-rails'
|
13
|
+
```
|
14
|
+
|
15
|
+
Then execute:
|
16
|
+
|
17
|
+
```sh
|
18
|
+
$ bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
And add your favourite flavour to your `application.js` file:
|
22
|
+
|
23
|
+
```javascript
|
24
|
+
//= require bliss
|
25
|
+
```
|
26
|
+
|
27
|
+
If you want to go with the shy version (it doesn't touch the host environment,
|
28
|
+
except by adding a global variable named `Bliss`), you might do want to add
|
29
|
+
this instead:
|
30
|
+
|
31
|
+
```javascript
|
32
|
+
//= require bliss.shy
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
You might also be interested in the `blissful-ujs` gem if you are looking to
|
37
|
+
replace `jquery-ujs`. Read more about it
|
38
|
+
[here](https://github.com/spicymagpie/blissful-ujs).
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
If you installed this library, then you might want to visit the
|
43
|
+
[documentation](http://blissfuljs.com). Basically, I might give you a tip:
|
44
|
+
|
45
|
+
```javascript
|
46
|
+
|
47
|
+
$.ready().then(function () {
|
48
|
+
console.log('Ok, you\'ve loaded Bliss.js successfully!')
|
49
|
+
})
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
Blissful documentation mentions which jQuery methods are similar. The difference
|
54
|
+
between those is the implementation: Bliss is only 3kb and might probably not
|
55
|
+
work well in older browsers without polyfills.
|
56
|
+
|
57
|
+
[See this](http://blissfuljs.com/#browser-support) if you have issues.
|
58
|
+
|
59
|
+
## Development
|
60
|
+
|
61
|
+
This gem is updated once a week. Basically, if we find changes in the JavaScript
|
62
|
+
library, we will put the new JavaScript files and upload a new gem. However, the
|
63
|
+
library was written for little to no maintenance, hence even if you do not see
|
64
|
+
activity in a month or so, it's safe to use.
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
Bug reports and pull requests are welcome on GitHub at
|
69
|
+
https://github.com/spicymagpie/blissful-rails.
|
70
|
+
|
71
|
+
## License
|
72
|
+
|
73
|
+
The gem is available as open source under the terms of the
|
74
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
75
|
+
|
76
|
+
## Thanks
|
77
|
+
|
78
|
+
Thanks to [Lea Verou](http://lea.verou.me/) for writing the library, and to
|
79
|
+
[Hendrik Kleinwächter](http://zeit.io/) for letting me experiment a little with
|
80
|
+
other things.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'blissful/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "blissful-rails"
|
8
|
+
spec.version = Blissful::Rails::VERSION
|
9
|
+
spec.authors = ["Spicy Magpie"]
|
10
|
+
spec.email = ["spicy.magpie@gmail.com"]
|
11
|
+
spec.summary = %q{A wrapper gem for Bliss.js}
|
12
|
+
spec.homepage = "https://github.com/spicymagpie/blissful-rails"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.require_paths = ["lib"]
|
16
|
+
|
17
|
+
spec.add_dependency "railties", "~> 4.0"
|
18
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
19
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
20
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
21
|
+
end
|
data/lib/blissful.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "blissful/rails/version"
|
2
|
+
|
3
|
+
module Blissful
|
4
|
+
module Rails
|
5
|
+
class << self
|
6
|
+
# Loading the heavenly library
|
7
|
+
def load!
|
8
|
+
if rails?
|
9
|
+
loader_for_rails
|
10
|
+
elsif sprockets?
|
11
|
+
loader_for_sprockets
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Paths
|
16
|
+
def gem_path
|
17
|
+
@gem_path ||= File.expand_path '..', File.dirname(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def assets_path
|
21
|
+
@assets_path ||= File.join gem_path, 'assets'
|
22
|
+
end
|
23
|
+
|
24
|
+
def javascripts_path
|
25
|
+
File.join assets_path, 'javascripts'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Environment detection helpers
|
29
|
+
def rails?
|
30
|
+
defined?(::Rails)
|
31
|
+
end
|
32
|
+
|
33
|
+
def sprockets?
|
34
|
+
defined?(::Sprockets)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Loader for Rails
|
38
|
+
def loader_for_rails
|
39
|
+
require 'blissful/rails/engine'
|
40
|
+
end
|
41
|
+
|
42
|
+
# Loader for Sprockets
|
43
|
+
def loader_for_sprockets
|
44
|
+
Sprockets.append_path(javascripts_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Blissful::Rails.load!
|
@@ -0,0 +1,722 @@
|
|
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
|
+
})();
|
626
|
+
|
627
|
+
(function($) {
|
628
|
+
"use strict";
|
629
|
+
|
630
|
+
if (!Bliss || Bliss.shy) {
|
631
|
+
return;
|
632
|
+
}
|
633
|
+
|
634
|
+
var _ = Bliss.property;
|
635
|
+
|
636
|
+
// Methods requiring Bliss Full
|
637
|
+
$.add({
|
638
|
+
// Clone elements, with events
|
639
|
+
clone: function () {
|
640
|
+
var clone = this.cloneNode(true);
|
641
|
+
var descendants = $.$("*", clone).concat(clone);
|
642
|
+
|
643
|
+
$.$("*", this).concat(this).forEach(function(element, i, arr) {
|
644
|
+
$.events(descendants[i], element);
|
645
|
+
});
|
646
|
+
|
647
|
+
return clone;
|
648
|
+
}
|
649
|
+
}, {array: false});
|
650
|
+
|
651
|
+
// Define the _ property on arrays and elements
|
652
|
+
|
653
|
+
Object.defineProperty(Node.prototype, _, {
|
654
|
+
// Written for IE compatability (see #49)
|
655
|
+
get: function getter () {
|
656
|
+
Object.defineProperty(Node.prototype, _, {
|
657
|
+
get: undefined
|
658
|
+
});
|
659
|
+
Object.defineProperty(this, _, {
|
660
|
+
value: new $.Element(this)
|
661
|
+
});
|
662
|
+
Object.defineProperty(Node.prototype, _, {
|
663
|
+
get: getter
|
664
|
+
});
|
665
|
+
return this[_];
|
666
|
+
},
|
667
|
+
configurable: true
|
668
|
+
});
|
669
|
+
|
670
|
+
Object.defineProperty(Array.prototype, _, {
|
671
|
+
get: function () {
|
672
|
+
Object.defineProperty(this, _, {
|
673
|
+
value: new $.Array(this)
|
674
|
+
});
|
675
|
+
|
676
|
+
return this[_];
|
677
|
+
},
|
678
|
+
configurable: true
|
679
|
+
});
|
680
|
+
|
681
|
+
// Hijack addEventListener and removeEventListener to store callbacks
|
682
|
+
|
683
|
+
if (self.EventTarget && "addEventListener" in EventTarget.prototype) {
|
684
|
+
var addEventListener = EventTarget.prototype.addEventListener,
|
685
|
+
removeEventListener = EventTarget.prototype.removeEventListener,
|
686
|
+
equal = function(callback, capture, l){
|
687
|
+
return l.callback === callback && l.capture == capture;
|
688
|
+
},
|
689
|
+
notEqual = function() { return !equal.apply(this, arguments); };
|
690
|
+
|
691
|
+
EventTarget.prototype.addEventListener = function(type, callback, capture) {
|
692
|
+
if (this[_] && callback) {
|
693
|
+
var listeners = this[_].bliss.listeners = this[_].bliss.listeners || {};
|
694
|
+
|
695
|
+
listeners[type] = listeners[type] || [];
|
696
|
+
|
697
|
+
if (listeners[type].filter(equal.bind(null, callback, capture)).length === 0) {
|
698
|
+
listeners[type].push({callback: callback, capture: capture});
|
699
|
+
}
|
700
|
+
}
|
701
|
+
|
702
|
+
return addEventListener.call(this, type, callback, capture);
|
703
|
+
};
|
704
|
+
|
705
|
+
EventTarget.prototype.removeEventListener = function(type, callback, capture) {
|
706
|
+
if (this[_] && callback) {
|
707
|
+
var listeners = this[_].bliss.listeners = this[_].bliss.listeners || {};
|
708
|
+
|
709
|
+
if (listeners[type]) {
|
710
|
+
listeners[type] = listeners[type].filter(notEqual.bind(null, callback, capture));
|
711
|
+
}
|
712
|
+
}
|
713
|
+
|
714
|
+
return removeEventListener.call(this, type, callback, capture);
|
715
|
+
};
|
716
|
+
}
|
717
|
+
|
718
|
+
// Set $ and $$ convenience methods, if not taken
|
719
|
+
self.$ = self.$ || $;
|
720
|
+
self.$$ = self.$$ || $.$;
|
721
|
+
|
722
|
+
})(Bliss);
|